Wofür werden in Java 8 funktionale Schnittstellen verwendet?

154

In Java 8 bin ich auf einen neuen Begriff gestoßen: "Funktionsschnittstelle". Ich konnte nur eine Verwendung finden, während ich mit Lambda-Ausdrücken arbeitete .

Java 8 bietet einige integrierte Funktionsschnittstellen. Wenn wir eine Funktionsschnittstelle definieren möchten, können wir die @FunctionalInterfaceAnmerkung verwenden. Damit können wir nur eine einzige Methode in der Schnittstelle deklarieren.

Beispielsweise:

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

Wie nützlich ist es in Java 8, außer nur mit Lambda-Ausdrücken zu arbeiten ?

(Die Frage hier unterscheidet sich von der, die ich gestellt habe. Sie fragt, warum wir funktionale Schnittstellen benötigen, wenn wir mit Lambda-Ausdrücken arbeiten. Meine Frage lautet: Warum haben funktionale Schnittstellen neben Lambda-Ausdrücken auch andere Verwendungsmöglichkeiten?)

Madhusudan
quelle
1
Es sieht doppelt so aus wie dieser Link. Sie sprechen auch darüber, warum es in Functional Interface nur eine Methode geben sollte. stackoverflow.com/questions/33010594/…
Kulbhushan Singh
1
@ KulbhushanSingh Ich sah diese Frage vor dem Posten ... Beide Fragen spüren Unterschied ...
Madhusudan

Antworten:

127

@FunctionalInterfaceAnmerkungen sind nützlich, um die Kompilierungszeit Ihres Codes zu überprüfen. Sie können nicht mehr als eine Methode haben neben static, defaultund abstrakte Methoden außer Kraft Methoden in ObjectIhrer @FunctionalInterfaceoder jede andere Schnittstelle als funktionale Schnittstelle verwendet.

Sie können jedoch Lambdas ohne diese Annotation verwenden und Methoden ohne @OverrideAnnotation überschreiben .

Aus Dokumenten

Eine funktionale Schnittstelle hat genau eine abstrakte Methode. Da Standardmethoden eine Implementierung haben, sind sie nicht abstrakt. Wenn eine Schnittstelle eine abstrakte Methode deklariert, die eine der öffentlichen Methoden von java.lang.Object überschreibt, zählt dies auch nicht für die Anzahl der abstrakten Methoden der Schnittstelle, da jede Implementierung der Schnittstelle eine Implementierung von java.lang.Object oder anderswo hat

Dies kann im Lambda-Ausdruck verwendet werden:

public interface Foo {
  public void doSomething();
}

Dies kann nicht im Lambda-Ausdruck verwendet werden:

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

Dies führt jedoch zu einem Kompilierungsfehler :

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

Ungültige Annotation '@FunctionalInterface'; Foo ist keine funktionale Schnittstelle

Sergii Bishyr
quelle
43
Um genauer zu sein, müssen Sie genau eine abstrakte Methode haben, die eine Methode java.lang.Objectin einer funktionalen Schnittstelle nicht überschreibt .
Holger
9
… Und es ist etwas anders, „nicht mehr als eine publicMethode außer staticund zu haben default“…
Holger
4
Ich verstehe immer noch keinen Grund, es zu haben. Warum sollte sich irgendjemand auf der Welt die Mühe machen, zu überprüfen, wie viele Methoden seine / ihre Schnittstelle hat? Marker-Schnittstellen haben immer noch einen Punkt und einen bestimmten Zweck. Die Dokumentation und die Antwort erklären nur, was es tut, nicht wie es überhaupt von Nutzen ist. Und "verwenden" ist genau das, was das OP gefragt hat. Daher würde ich diese Antwort nicht empfehlen.
Saran3h
1
@VNT Der Kompilierungsfehler ruft die Clients dieser Schnittstelle ab, aber nicht die Schnittstelle selbst kann sich ändern. Mit dieser Anmerkung befindet sich der Kompilierungsfehler auf der Schnittstelle, sodass Sie sicherstellen, dass niemand die Clients Ihrer Schnittstelle beschädigt.
Sergii Bishyr
2
Dies zeigt, wie man sie benutzt, erklärt aber nicht, warum wir sie brauchen.
Scheich
14

Die Dokumentation macht in der Tat einen Unterschied zwischen dem Zweck

Ein informativer Anmerkungstyp, der angibt, dass eine Schnittstellentypdeklaration eine funktionale Schnittstelle im Sinne der Java-Sprachspezifikation sein soll.

und der Anwendungsfall

Beachten Sie, dass Instanzen von Funktionsschnittstellen mit Lambda-Ausdrücken, Methodenreferenzen oder Konstruktorreferenzen erstellt werden können.

deren Wortlaut andere Anwendungsfälle im Allgemeinen nicht ausschließt. Da der Hauptzweck darin besteht, eine funktionale Schnittstelle anzugeben, lautet Ihre eigentliche Frage: "Gibt es andere Anwendungsfälle für funktionale Schnittstellen als Lambda-Ausdrücke und Methoden- / Konstruktorreferenzen?"

Da die funktionale Schnittstelle ein Java-Sprachkonstrukt ist, das durch die Java-Sprachspezifikation definiert ist, kann nur diese Spezifikation diese Frage beantworten:

JLS §9.8. Funktionsschnittstellen :

Zusätzlich zum üblichen Prozess des Erstellens einer Schnittstelleninstanz durch Deklarieren und Instanziieren einer Klasse (§15.9) können Instanzen von Funktionsschnittstellen mit Methodenreferenzausdrücken und Lambda-Ausdrücken (§15.13, §15.27) erstellt werden.

Die Java-Sprachspezifikation sagt also nichts anderes aus. Der einzige in diesem Abschnitt erwähnte Anwendungsfall ist das Erstellen von Schnittstelleninstanzen mit Methodenreferenzausdrücken und Lambda-Ausdrücken. (Dies schließt Konstruktorreferenzen ein, da sie in der Spezifikation als eine Form des Methodenreferenzausdrucks angegeben sind.)

Also in einem Satz, nein, es gibt keinen anderen Anwendungsfall dafür in Java 8.

Holger
quelle
Vielleicht fragen Sie nur nach etwas zu viel oder irrelevantem (Sie können sich dafür entscheiden, nicht zu antworten), aber was würden Sie vorschlagen, wenn jemand ein Dienstprogramm erstellt public static String generateTaskId(), anstatt es "funktionaler" zu machen , wenn jemand anderes es wie public class TaskIdSupplier implements Supplier<String>bei der getMethode mit der Methode geschrieben hat bestehende Generationsimplementierung. Ist das ein Missbrauch von Funktionsschnittstellen, insbesondere die Wiederverwendung der Supplieraus dem JDK integrierten? PS: Ich könnte keinen besseren Ort / Fragen und Antworten finden, um dies zu fragen. Gerne migrieren, wenn Sie vorschlagen könnten.
Naman
1
@Naman Sie machen die Utility-Methode nicht funktionaler, wenn Sie eine benannte Klasse erstellen TaskIdSupplier. Die Frage ist nun, warum Sie die benannte Klasse erstellt haben. Es gibt Szenarien, in denen ein solcher benannter Typ benötigt wird, z. B. wenn Sie das Auffinden der Implementierung über unterstützen möchten ServiceLoader. Es ist nichts Falsches daran, es Supplierdann implementieren zu lassen . Aber wenn Sie es nicht brauchen, erstellen Sie es nicht. Wenn Sie nur eine benötigen Supplier<String>, reicht es bereits aus, diese zu verwenden, DeclaringClass::generateTaskIdund die Notwendigkeit einer expliziten Klasse zu eliminieren, ist der Punkt dieser Sprachfunktion.
Holger
Um ehrlich zu sein, suchte ich nach einer Rechtfertigung für eine Empfehlung, die ich weitergab. Aus irgendeinem Grund hatte ich bei der Arbeit nicht wirklich das Gefühl, dass die TaskIdSupplierImplementierung die Mühe wert war, aber dann ServiceLoaderging mir das Konzept völlig aus dem Kopf. Während dieser Diskussionen stießen wir auf einige Fragen wie: Was nützt Supplierdie publicExistenz, wenn man seine eigenen Schnittstellen entwickeln kann? und warum nicht public static Supplier<String> TASK_ID_SUPPLIER = () ->...als globale Konstante? . (1/2)
Naman
1
@Naman Die idiomatische Art, Funktionen in Java darzustellen, sind Methoden, und die Auswertung dieser Funktionen ist identisch mit dem Aufrufen. Niemals sollte ein Entwickler gezwungen sein , zu tun , variable.genericMethodName(args)statt meaningfulMethodName(args). Die Verwendung eines Klassentyps zur Darstellung einer Funktion, sei es über einen Lambda-Ausdruck / eine Methodenreferenz oder eine manuell erstellte Klasse, ist nur ein Mittel , um die Funktion weiterzugeben (sofern in Java keine echten Funktionstypen vorhanden sind). Dies sollte nur bei Bedarf erfolgen.
Holger
1
Wenn ein kleines Codefragment nur herumgereicht wird, können Sie einen Lambda-Ausdruck erstellen, der es einschließt. Wenn Sie es auch wie eine Methode aufrufen müssen (dies schließt Szenarien mit Testbedarf ein, wenn das Codefragment nicht trivial ist), erstellen Sie eine benannte Methode, die aufgerufen werden kann, und verwenden Sie eine Methodenreferenz oder einen Lambda-Ausdruck / eine explizite Klasse Einkapseln eines Anrufs, um ihn bei Bedarf weiterzuleiten. Konstanten sind nur dann nützlich, wenn Sie der Effizienz von in Ihren Code eingebetteten Lambda-Ausdrücken oder Methodenreferenzen nicht vertrauen. Mit anderen Worten, sie werden fast nie benötigt.
Holger
12

Wie andere gesagt haben, ist eine funktionale Schnittstelle eine Schnittstelle, die eine Methode verfügbar macht. Es kann mehr als eine Methode geben, aber alle anderen müssen eine Standardimplementierung haben. Der Grund, warum es als "funktionale Schnittstelle" bezeichnet wird, ist, dass es effektiv als Funktion fungiert. Da Sie Schnittstellen als Parameter übergeben können, sind Funktionen jetzt "erstklassige Bürger" wie in funktionalen Programmiersprachen. Dies hat viele Vorteile, und Sie werden sie ziemlich oft sehen, wenn Sie die Stream-API verwenden. Natürlich sind Lambda-Ausdrücke die offensichtlichste Hauptverwendung für sie.

Sina Madani
quelle
10

Überhaupt nicht. Lambda-Ausdrücke sind der einzige Punkt dieser Anmerkung.

Louis Wasserman
quelle
6
Nun, Lamdbas funktionieren auch ohne die Anmerkung. Es ist eine Behauptung @Override, die dem Compiler nur mitteilen möchte , dass Sie beabsichtigt haben, etwas zu schreiben, das "funktionsfähig" ist (und eine Fehlermeldung erhalten, wenn Sie ausrutschen).
Thilo
1
Auf den Punkt und die richtige Antwort, wenn auch etwas kurz. Ich nahm mir die Zeit, um eine ausführlichere Antwort hinzuzufügen , die dasselbe mit mehr Worten sagt…
Holger
5

Ein Lambda-Ausdruck kann einem funktionalen Schnittstellentyp zugewiesen werden, ebenso wie Methodenreferenzen und anonyme Klassen.

Eine nette Sache über die spezifischen funktionalen Schnittstellen in java.util.functionist , dass sie zusammengesetzt werden , um neue Funktionen zu erstellen (wie Function.andThenund Function.compose, Predicate.andusw.) aufgrund der praktisch Standard Methoden , die sie enthalten.

Hank D.
quelle
Sie sollten diesen Kommentar näher erläutern. Was ist mit Methodenreferenzen und neuen Funktionen?
K.Nicholas
5

Eine Schnittstelle mit nur einer abstrakten Methode wird als funktionale Schnittstelle bezeichnet. Die Verwendung von @FunctionalInterface ist nicht obligatorisch, es wird jedoch empfohlen, es mit funktionalen Schnittstellen zu verwenden, um zu vermeiden, dass versehentlich zusätzliche Methoden hinzugefügt werden. Wenn die Schnittstelle mit der Annotation @FunctionalInterface versehen ist und wir versuchen, mehr als eine abstrakte Methode zu verwenden, wird ein Compilerfehler ausgegeben.

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;
        }
    }
Akhilesh
quelle
3

Funktionsschnittstelle:

  • Eingeführt in Java 8
  • Schnittstelle, die eine "einzelne abstrakte" Methode enthält.

Beispiel 1:

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

Beispiel 2:

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

Beispiel 3:

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

Java8 Annotation - @FunctionalInterface

  • Anmerkungsprüfung, dass die Schnittstelle nur eine abstrakte Methode enthält. Wenn nicht, Fehler auslösen.
  • Obwohl @FunctionalInterface fehlt, handelt es sich immer noch um eine funktionale Schnittstelle (wenn eine einzelne abstrakte Methode vorhanden ist). Die Anmerkung hilft, Fehler zu vermeiden.
  • Die Funktionsschnittstelle verfügt möglicherweise über zusätzliche statische und Standardmethoden.
  • zB Iterable <>, Comparable <>, Comparator <>.

Anwendungen der funktionalen Schnittstelle:

  • Methodenreferenzen
  • Lambda-Ausdruck
  • Konstruktorreferenzen

Um funktionale Schnittstellen zu lernen, lernen Sie die ersten Standardmethoden in der Schnittstelle. Nach dem Erlernen der funktionalen Schnittstelle ist es für Sie einfach, die Methodenreferenz und den Lambda-Ausdruck zu verstehen

Ketan
quelle
Sollten Ihre ersten beiden Beispiele das Schlüsselwort "abstrakt" enthalten?
sofs1
1
@ sofs1 In Schnittstellen deklarierte Methoden sind standardmäßig sowohl öffentlich als auch abstrakt. Bei Methoden in der abstrakten Klasse müssen Sie das Schlüsselwort abstract verwenden. Es ist jedoch in Ordnung, das abstrakte Schlüsselwort auch für Methoden in der Schnittstelle zu verwenden. Sie haben es aus Gründen der Kompatibilität mit älteren Java-Versionen zugelassen, aber es wird davon abgeraten.
Ketan
2

Sie können Lambda in Java 8 verwenden

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");
}

Weitere Informationen zu Java Lambdas und FunctionalInterfaces

Websterix
quelle
1

@FunctionalInterface ist eine neue Annotation, die mit Java 8 veröffentlicht wurde und Zieltypen für Lambda-Ausdrücke bereitstellt und zur Überprüfung der Kompilierungszeit Ihres Codes verwendet wird.

Wenn Sie es verwenden möchten:

1- Ihre Schnittstelle darf nicht mehr als eine abstrakte Methode haben, da sonst ein Kompilierungsfehler auftritt.

1- Ihre Schnittstelle sollte rein sein, was bedeutet, dass die funktionale Schnittstelle von zustandslosen Klassen implementiert werden soll. Ein Beispiel für eine reine ComparatorSchnittstelle ist , da sie nicht vom Status des Implementierers abhängt. In diesem Fall wird kein Kompilierungsfehler angegeben, aber in vielen Fällen von Ihnen wird nicht in der Lage sein, Lambda mit dieser Art von Schnittstellen zu verwenden

Das java.util.functionPaket enthält verschiedene Mehrzweck funktionelle Schnittstellen wie Predicate, Consumer, Function, und Supplier.

Bitte beachten Sie auch, dass Sie Lambdas ohne diese Anmerkung verwenden können.

Ahmad Al-Kurdi
quelle
1

Neben anderen Antworten denke ich, dass der Hauptgrund dafür, "warum eine andere funktionale Schnittstelle als direkt mit Lambda-Ausdrücken verwendet wird", mit der Natur der objektorientierten Java-Sprache zusammenhängen kann.

Die Hauptattribute von Lambda-Ausdrücken sind: 1. Sie können um 2. herum übergeben werden und können in Zukunft zu einer bestimmten Zeit (mehrmals) ausgeführt werden. Um diese Funktion in Sprachen zu unterstützen, befassen sich einige andere Sprachen einfach mit dieser Angelegenheit.

Beispielsweise kann in Java Script eine Funktion (anonyme Funktion oder Funktionsliterale) als Objekt adressiert werden. Sie können sie also einfach erstellen und sie können auch einer Variablen zugewiesen werden und so weiter. Beispielsweise:

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

oder über ES6 können Sie eine Pfeilfunktion verwenden.

const myFunction = ... => ...

Bisher haben Java-Sprachdesigner nicht akzeptiert, die genannten Funktionen auf diese Weise zu handhaben (funktionale Programmiertechniken). Sie glauben, dass die Java-Sprache objektorientiert ist, und sollten dieses Problem daher mithilfe objektorientierter Techniken lösen. Sie möchten nicht auf die Einfachheit und Konsistenz der Java-Sprache verzichten.

Daher verwenden sie Schnittstellen. Wenn ein Objekt einer Schnittstelle mit nur einer Methode (ich meine funktionale Schnittstelle) benötigt wird, können Sie es durch einen Lambda-Ausdruck ersetzen. Sowie:

ActionListener listener = event -> ...;
MMKarami
quelle