2015-09-12 13 views
13

Ich tauche derzeit tiefer in Java 8 Features wie Lambdas und Methodenreferenzen. Spielen um ein bisschen brachte mich auf das folgende Beispiel:Warum kompiliert diese Java 8 Methodenreferenz?

public class ConsumerTest { 

    private static final String[] NAMES = {"Tony", "Bruce", "Steve", "Thor"}; 

    public static void main(String[] args) { 
     Arrays.asList(NAMES).forEach(Objects::requireNonNull); 
    } 
} 

Meine Frage ist:

Warum wird die Linie in der Hauptmethode kompilieren?

Wenn ich die Sache richtig verstanden habe, muss die Signatur der referenzierten Methode der SAM-Signatur der funktionalen Schnittstelle entsprechen. In diesem Fall benötigt der Verbraucher die folgende Signatur:

void accept(T t); 

jedoch die requireNonNull Methode gibt T statt Leere:

public static <T> T requireNonNull(T obj) 
+4

Betrachte 'myList.forEach (myOtherList :: add)'. Ist es dir egal, dass 'List.add' 'boolean' zurückgibt, was immer 'wahr' ist? Es wäre ein Schmerz, wenn 'myOtherList :: add' nicht funktioniert. –

Antwort

13

Die Java Language Specification Version sagt 8 in 15.13.2:

Ein Methodenreferenzausdruck ist in einem Zuweisungskontext, Aufrufkontext oder Castingkontext mit einem Zieltyp T kompatibel, wenn T eine Funktion ist len Schnittstellentyp (§9.8) und der Expression mit dem Funktionstyp des Erdbodens Zielart abgeleitet von T. kongruenten

[..]

Ein Expressionsverfahren Referenz ist mit einem Funktionstyp, wenn beide der kongruenten Folgende sind wahr:

  • Der Funktionstyp identifiziert eine einzelne Kompilierzeit-Deklaration, die der Referenz entspricht.
  • Einer der folgenden Punkte zutrifft:
    • Das Ergebnis der Funktion Typ void ist.
    • Das Ergebnis des Funktionstyps ist R, und das Ergebnis der Anwendung der Capture-Konvertierung (§5.1.10) auf den Rückgabetyp des Aufruftyps (§15.12.2.6) der ausgewählten Kompilierzeitdeklaration ist R '(wobei R das Ziel ist) Typ, der verwendet werden kann, um R ') abzuleiten, und weder R noch R' ist ungültig, und R 'ist mit R in einem Zuordnungskontext kompatibel.

(Hervorhebung von mir)

Die Tatsache, das Ergebnis der Funktion Typ void ist, genügt es, es zu ermöglichen, anzupassen.

JLS 15.12.2.5 erwähnt auch speziell die Verwendung von void beim Abgleich von Methoden. Für den Lambda-Ausdruck gibt es den Begriff eines void-kompatiblen Blocks (15.27.2), auf den in 15.12.2.1 verwiesen wird, aber es gibt keine äquivalente Definition für Methodenreferenzen.

Ich konnte keine spezifischere Erklärung finden (aber die JLS ist eine harte Nuss zu knacken, so dass ich vielleicht einige relevante Abschnitte vermisse), aber ich nehme an, dass dies mit der Tatsache zu tun hat, dass Sie auch sind darf nicht-void-Methoden als eigenständige Anweisung aufrufen (ohne Zuweisung, return usw.)).

JLS 15.13.3 Run-Time Evaluation of Method References sagt auch:

Für die Zwecke der Kompilierung-Ergebnis, wobei das Verfahren Aufrufausdruck ist ein Ausdruck Anweisung, wenn das Ergebnis des Aufrufs Methode ist nichtig und die Expression einer return-Anweisung, wenn die Das Ergebnis der Aufrufmethode ist nicht void.

Die Wirkung dieser Bestimmung, wenn die Übersetzungszeit Erklärung des Verfahrens Referenzsignatur polymorph ist, ist, dass:

  • die Typen der Parameter für den Methodenaufruf die Typen der entsprechenden Argumente . Der Methodenaufruf ist entweder void oder hat den Rückgabetyp Object, abhängig davon, ob die Aufrufmethode, die den Methodenaufruf einschließt, ungültig ist oder einen Rückgabetyp hat.

So der erzeugte Methodenaufruf wird ungültig den Funktionstyp entspricht.

3

Abgesehen von dem, was er sagt, in genau die Antwort von @ Mark Rotteveel, kompiliert es, weil in Java Sie das Ergebnis eines Methodenaufruf ignorieren kann, wie im folgenden Beispiel:

Map<String, String> map = new HashMap<>(); 

map.put("1", "a"); 

map.put("1", "A"); // Who cares about the returned value "a"? 

Wie Sie nicht sind Zurückgeben von irgendetwas in der forEach() Verbraucher-Block, dann ist es gemäß der Spezifikation gültig.

Verwandte Themen