Java 8'de kullanılan işlevsel arabirimler nelerdir?


154

Java 8'de yeni bir terimle karşılaştım: "işlevsel arayüz". Lambda ifadeleriyle çalışırken sadece bir kullanım alanı bulabildim .

Java 8 bazı yerleşik işlevsel arabirimler sağlar ve herhangi bir işlevsel arabirimi tanımlamak istiyorsak, @FunctionalInterfaceek açıklamadan yararlanabiliriz. Arayüzde sadece tek bir yöntem bildirmemize izin verecektir.

Örneğin:

@FunctionalInterface
interface MathOperation {
    int operation(int a, int b);
}

Java 8'de lambda ifadeleriyle çalışmaktan ne kadar yararlı ?

(Soru burada sordum birinden farklıdır Bu lambda ifadeleri ile çalışırken biz fonksiyonel arayüzler neden ihtiyaç istiyor Sorum şu:.. Diğer kullanımları fonksiyonel arayüzler lambda ifadeleri ile yanında var mı neden?)


1
Bu bağlantıya duplcate görünüyor. Ayrıca, İşlevsel Arayüzde neden sadece bir yöntem olması gerektiğinden de bahsediyorlar. stackoverflow.com/questions/33010594/…
Kulbhushan Singh

1
@KulbhushanSingh Göndermeden önce bu soruyu gördüm ... Her iki soru da farkı fark ediyor ...
Madhusudan

Yanıtlar:


127

@FunctionalInterfaceek açıklama, kodunuzun derleme zamanı kontrolü için kullanışlıdır. Birden fazla yanında yöntemi olamaz static, defaultve soyut yöntemler ki geçersiz kılma yöntemleri Objectiçinde sizin @FunctionalInterfaceveya fonksiyonel bir arayüz olarak kullanılan herhangi bir başka arayüz.

Ancak bu açıklamaları olmadan lambdaları kullanabilir ve açıklama olmadan yöntemleri geçersiz kılabilirsiniz @Override.

Dokümanlardan

işlevsel bir arayüzün tam olarak bir soyut yöntemi vardır. Varsayılan yöntemlerin bir uygulaması olduğundan, bunlar soyut değildir. Bir arabirim, genel java.lang.Object yöntemlerinden birini geçersiz kılan soyut bir yöntem bildirirse, arabirimin herhangi bir uygulamasının java.lang.Object veya başka bir yerde bir uygulaması olacağından, arabirimin soyut yöntem sayımına da sayılmaz.

Bu lambda ifadesinde kullanılabilir :

public interface Foo {
  public void doSomething();
}

Bu lambda ifadesinde kullanılamaz :

public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

Ancak bu derleme hatası verecektir :

@FunctionalInterface
public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

Geçersiz '@FunctionalInterface' ek açıklaması; Foo fonksiyonel bir arayüz değil


43
Daha kesin olmak gerekirse, işlevsel bir arabirimdeki bir yöntemi geçersiz kılmayan tam olarak bir soyut yönteme sahip olmanız gerekir java.lang.Object.
Holger

9
… Ve “ publicyanı sıra birden fazla yöntemin olmaması” biraz farklı staticve default……
Holger

4
Hala sahip olmanın herhangi bir noktasını anlamıyorum. Neden yeryüzündeki herhangi biri arayüzünün kaç yönteminin olduğunu kontrol etmekten rahatsız olur. Marker arayüzlerinin hala bir amacı ve belirli bir amacı vardır. Dokümantasyon ve cevap, ne işe yaradığını değil, sadece ne yaptığını açıklıyor. Ve "kullanım" OP'nin tam olarak sorduğu şeydir. Bu yüzden bu cevabı tavsiye etmem.
saran3h

1
@VNT derleme hatası bu arabirimin istemcilerini almak, ancak arabirimin kendisi değişebilir. Bu ek açıklama ile derleme hatası arabirimdedir, böylece kimsenin arabiriminizin istemcilerini bozmayacağından emin olursunuz.
Sergii Bishyr

2
Bu, onları nasıl kullanacağınızı gösterir, ancak neden onlara ihtiyaç duyduğumuzu açıklamaz.
şeyh

14

Belgeler gerçekten amacı arasındaki bir farktır

Arabirim türü bildiriminin Java Dil Belirtimi tarafından tanımlanan işlevsel bir arabirim olması gerektiğini belirtmek için kullanılan bilgilendirici ek açıklama türü .

ve kullanım durumu

Fonksiyonel arayüz örneklerinin lambda ifadeleri, yöntem referansları veya yapıcı referansları ile oluşturulabileceğini unutmayın.

ifadeleri genel olarak diğer kullanım durumlarını engellemeyen. Birincil amaç işlevsel bir arayüz belirtmek olduğundan, asıl sorunuz Lambda ifadeleri ve yöntem / kurucu referansları dışında fonksiyonel arayüzler için başka kullanım durumları var mı?”

Yana işlevsel arayüzü Java Dil Şartnamede tarafından tanımlanan bir Java dil oluşumudur, şartname bu soruyu cevap verebilecek tek o:

JLS §9.8. Fonksiyonel Arayüzler :

...

Bir sınıfı bildirerek ve somutlaştırarak bir arabirim örneği oluşturma işlemine ek olarak (§15.9), yöntem başvuru ifadeleri ve lambda ifadeleriyle fonksiyonel arabirimlerin örnekleri oluşturulabilir (§15.13, §15.27).

Java Dil Belirtimi aksini söylemez, bu bölümde belirtilen tek kullanım örneği, yöntem başvuru ifadeleri ve lambda ifadeleriyle arabirim örnekleri oluşturmaktır. (Spesifikasyonda yöntem referans ifadesinin bir biçimi olarak belirtildikleri için yapıcı referanslarını içerir).

Yani bir cümlede, hayır, Java 8'de bunun için başka bir kullanım durumu yoktur.


Sadece çok fazla küçük için sorarak veya ilgisiz olabilir (eğer cevap seçebilirsiniz), ancak birisi bir yarar oluştururken neler öneriyorsun public static String generateTaskId()daha "işlevsel" başkasının gibi yazmak seçti yapmak versus public class TaskIdSupplier implements Supplier<String>ile getkullanarak yöntemle mevcut nesil uygulaması. Bu, işlevsel arayüzlerin yanlış kullanımı mı, özellikle SupplierJDK yerleşikinden yeniden mi kullanılıyor? Not: Bunu sormak için daha iyi bir yer / soru-cevap bulamadım. Eğer önerebilirseniz göç mutlu.
Naman

1
@Naman, adlandırılmış bir sınıf oluşturduğunuzda yardımcı program yöntemini daha işlevsel hale getirmiyorsunuz TaskIdSupplier. Şimdi soru, adlandırılmış sınıfı neden oluşturduğunuz. Böyle bir adlandırılmış türün gerekli olduğu senaryolar vardır; örneğin, uygulamanın aracılığıyla bulunmasını desteklemek istediğinizde ServiceLoader. Uygulamasına izin vermede yanlış bir şey yok Supplier. Ama ihtiyacınız olmadığında yaratmayın. Sadece a'ya ihtiyacınız olduğunda Supplier<String>, zaten kullanmak yeterlidir DeclaringClass::generateTaskIdve açık bir sınıfa olan ihtiyacı ortadan kaldırmak bu dil özelliğinin ana konusudur.
Holger

Dürüst olmak gerekirse, aktardığım bir tavsiye için bir gerekçe arıyordum. Bazı nedenlerden dolayı TaskIdSupplieruygulamanın gerçekten çabaya değdiğini hissetmedim , ama sonra ServiceLoadertamamen aklımdan çıkma kavramı . Şu gibi yapıyorduk Bu tartışmalar sırasında birkaç soru Karşılaşılan kullanılmasıdır Ne Supplierbireyin publicbiri devam edin ve kendi arayüzlerini gelişebilir zaman varlığı? ve Neden public static Supplier<String> TASK_ID_SUPPLIER = () ->...küresel bir sabit olarak olmasın ? . (1/2)
Naman

1
@Naman, Java'daki işlevleri temsil etmenin deyimsel yoludur ve bu işlevleri değerlendirmek, onları çağırmakla aynıdır. Bir geliştirici yapmak zorunda olmalıdır asla variable.genericMethodName(args)yerine meaningfulMethodName(args). Lambda ifadesi / yöntem başvurusu veya elle oluşturulmuş bir sınıf aracılığıyla bir işlevi temsil etmek için bir sınıf türü kullanmak, yalnızca işlevi iletmek için bir araçtır (Java'da gerçek işlev türlerinin yokluğunda). Bu sadece gerektiğinde yapılmalıdır.
Holger

1
Yalnızca etrafta iletilen küçük bir kod parçanız olduğunda, onu çevreleyen bir lambda ifadesi oluşturabilirsiniz. Her zaman bir yöntem gibi çağırmaya ihtiyaç duyulduğunda (bu, kod parçası önemsiz olmadığında test gerektiren senaryolar içerir), çağrılabilecek adlandırılmış bir yöntem oluşturun ve bir yöntem başvurusu veya lambda ifadesi / açık sınıf kullanın bir çağrıyı kapsüllemek, gerektiğinde etrafta dolaşmak. Sabitler yalnızca, kodunuza gömülü lambda ifadelerinin veya yöntem referanslarının verimliliğine güvenmediğinizde, diğer bir deyişle neredeyse hiç gerekmediğinde yararlıdır.
Holger

12

Diğerlerinin söylediği gibi, fonksiyonel bir arayüz bir yöntemi ortaya çıkaran bir arayüzdür. Birden fazla yöntemi olabilir, ancak diğerlerinin hepsinin varsayılan bir uygulaması olmalıdır. "İşlevsel arabirim" olarak adlandırılmasının nedeni, etkin bir işlev işlevi görmesidir. Arabirimleri parametre olarak geçirebildiğiniz için, işlevler artık işlevsel programlama dillerinde olduğu gibi "birinci sınıf vatandaş" tır. Bunun birçok faydası vardır ve Akış API'sını kullanırken bunları oldukça fazla göreceksiniz. Tabii ki, lambda ifadeleri onlar için ana bariz kullanımdır.


10

Bir şey değil. Lambda ifadeleri bu ek açıklamanın tek ve tek noktasıdır.


6
Lamdbas ek açıklama olmadan da çalışır. Bu @Override, derleyiciye "işlevsel" bir şey yazmak istediğinizi bildirmek (ve kaymanız durumunda bir hata almak) gibi bir iddiadır .
Thilo

1
Düz ve doğru cevap, biraz kısa olsa da. Daha fazla kelimeyle aynı şeyi söyleyerek daha ayrıntılı bir cevap eklemek için zaman
Holger

5

Bir lambda ifadesi işlevsel bir arabirim türüne atanabilir, ancak yöntem başvuruları ve anonim sınıflar da atanabilir.

Belirli fonksiyonel arayüzler hakkında güzel bir şey java.util.functiononlar (gibi yeni işlevler oluşturmak için oluşabilir olmasıdır Function.andThenve Function.compose, Predicate.andvs.) nedeniyle içerdikleri kullanışlı varsayılan yöntemlerine ilişkindir.


Bu yorumu daha ayrıntılı olarak açıklamalısınız. Yöntem başvuruları ve yeni işlevler ne olacak?
K.Nicholas

5

Yalnızca bir soyut yönteme sahip bir arabirime İşlevsel Arabirim adı verilir. @FunctionalInterface kullanmak zorunlu değildir, ancak yanlışlıkla ekstra yöntemlerin eklenmesini önlemek için işlevsel arabirimlerle kullanmak en iyi uygulamadır. Arayüz @FunctionalInterface ek açıklaması ile açıklanırsa ve birden fazla soyut yönteme sahip olmaya çalışırsak, derleyici hatası atar.

package com.akhi;
    @FunctionalInterface
    public interface FucnctionalDemo {

      void letsDoSomething();
      //void letsGo();      //invalid because another abstract method does not allow
      public String toString();    // valid because toString from Object 
      public boolean equals(Object o); //valid

      public static int sum(int a,int b)   // valid because method static
        {   
            return a+b;
        }
        public default int sub(int a,int b)   //valid because method default
        {
            return a-b;
        }
    }

3

Fonksiyonel arayüzü:

  • Java 8'de tanıtıldı
  • "Tek bir özet" yöntemi içeren arayüz.

Örnek 1:

   interface CalcArea {   // --functional interface
        double calcArea(double rad);
    }           

Örnek 2:

interface CalcGeometry { // --functional interface
    double calcArea(double rad);
    default double calcPeri(double rad) {
        return 0.0;
    }
}       

Örnek 3:

interface CalcGeometry {  // -- not functional interface
    double calcArea(double rad);
    double calcPeri(double rad);
}   

Java8 ek açıklaması - @FunctionalInterface

  • Ek açıklama, arabirimin yalnızca bir soyut yöntem içerdiğini kontrol edin. Değilse, hatayı yükseltin.
  • @FunctionalInterface eksik olsa da, hala işlevsel bir arayüzdür (tek bir soyut yönteme sahipse). Ek açıklama hataların önlenmesine yardımcı olur.
  • İşlevsel arabirimin ek statik ve varsayılan yöntemleri olabilir.
  • örneğin Tekrarlanabilir <>, Karşılaştırılabilir <>, Karşılaştırıcı <>.

Fonksiyonel Arayüz Uygulamaları:

  • Yöntem referansları
  • Lambda İfadesi
  • Yapıcı referansları

Fonksiyonel arayüzleri öğrenmek, arayüzde ilk varsayılan yöntemleri öğrenmek ve fonksiyonel arayüzü öğrendikten sonra, yöntem referansını ve lambda ifadesini anlamak sizin için kolay olacaktır


İlk iki örneğinizde 'özet' anahtar kelimesi olmalı mı?
sofs1

1
@ sofs1 Arabirimlerde bildirilen yöntemler varsayılan olarak hem genel hem de özettir. Soyut sınıftaki yöntemlerde abstract anahtar sözcüğünü kullanmanız gerekir. Bununla birlikte, arabirimdeki yöntemler için soyut anahtar kelime kullanmak da iyidir. Eski java sürümünün uyumluluğu için izin verdiler, ancak önerilmez.
Ketan

2

Java 8'de lambda kullanabilirsiniz

public static void main(String[] args) {
    tentimes(inputPrm - > System.out.println(inputPrm));
    //tentimes(System.out::println);  // You can also replace lambda with static method reference
}

public static void tentimes(Consumer myFunction) {
    for (int i = 0; i < 10; i++)
        myFunction.accept("hello");
}

Java Lambdas ve FunctionalInterfaces hakkında daha fazla bilgi için


1

@FunctionalInterface Java 8 ile yeni bir ek açıklama yayınlanır ve lambda ifadeleri için hedef türleri sağlar ve kodunuzun derleme zamanı kontrolünde kullanılır.

Kullanmak istediğinizde:

1- Kişisel arayüzü olmamalıdır birden fazla soyut yöntemler var, aksi takdirde derleme hatası verilecektir.

1- Kişisel arayüzü Should fonksiyonel arayüz vatansız sınıf uygulanması amaçlanan, yani saf, saf exmple olan Comparatorbu durumda, uygulayıcılar devlete bağlı olmayan onun yüzünden arayüzü Hayır verilecek derleme hatası, ancak birçok durumda size Bu tür arayüzlerle lambda kullanamazsınız.

java.util.functionPaket çeşitli genel amaçlı fonksiyonel arayüzler gibi içerir Predicate, Consumer, Function, ve Supplier.

Ayrıca bu ek açıklama olmadan lambdas kullanabileceğinizi lütfen unutmayın.


1

Diğer cevapların yanı sıra, "neden doğrudan lambda ifadeleri dışında İşlevsel Arayüz kullanmanın" ana nedeninin Nesne Odaklı Java dilinin doğası ile ilgili olabileceğini düşünüyorum.

Lambda ifadelerinin ana özellikleri şunlardır: 1. Bunlar 2. civarında geçirilebilir ve gelecekte belirli bir zamanda (birkaç kez) yürütülebilirler. Şimdi bu özelliği dillerde desteklemek için, diğer bazı diller sadece bu konuyla ilgilenmektedir.

Örneğin Java Komut Dosyasında, bir işlev (Anonim işlev veya İşlev değişmez değerleri) nesne olarak adreslenebilir. Böylece, bunları basitçe oluşturabilirsiniz ve ayrıca bir değişkene atanabilirler. Örneğin:

var myFunction = function (...) {
    ...;
}
alert(myFunction(...));

veya ES6 aracılığıyla bir ok işlevi kullanabilirsiniz.

const myFunction = ... => ...

Java dil tasarımcıları şimdiye kadar bahsedilen özellikleri bu şekilde ele almayı kabul etmediler (fonksiyonel programlama teknikleri). Java dilinin Nesneye Yönelik olduğuna inanırlar ve bu nedenle bu sorunu Nesne Yönelimli tekniklerle çözmeleri gerekir. Java dilinin sadeliğini ve tutarlılığını kaçırmak istemiyorlar.

Bu nedenle, arayüzleri kullanırlar, çünkü sadece bir yöntemle bir arayüz nesnesi (yani fonksiyonel arayüz) gerektiğinde bunu bir lambda ifadesi ile değiştirebilirsiniz. Gibi:

ActionListener listener = event -> ...;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.