Compiler, der die Funktionsschnittstelle System.out :: println nicht ableitet

8

Ich habe eine überladene Methode, die zwei verschiedene funktionale Schnittstellen als Parameter verwendet ( Runnble und Supplier ). System.out.println ist eindeutig nur mit Runnable kompatibel, da es sich um eine void -Methode handelt. Der Compiler behauptet jedoch immer noch, dass der Aufruf mehrdeutig ist. Wie ist das möglich?

%Vor%

Compiler-Ausgabe:

%Vor%

Sollte der Compiler aufgrund des zweiten Fehlers ( argument mismatch, void cannot be converted to R ) nicht in der Lage sein, den Aufruf zu disambiguieren? Das würde dann beide Compiler-Fehler behandeln (da es weder zweideutig ist, noch wird es versuchen, void in R umzuwandeln).

Und warum können () -> {} und System.out::flush auflösen? Sie haben die gleiche Signatur wie System.out.println . Zugegeben, dass System.out.println mit Versionen überladen ist, die ein Argument verwenden, aber keine dieser überladenen Versionen mit Supplier oder Runnable übereinstimmt, daher sehe ich nicht, wie sie hier relevant wären.

BEARBEITEN:

Es scheint, dass das kompiliert und mit Eclipse-Compiler . Welcher Compiler ist korrekt oder ist das Verhalten erlaubt?

    
Yosef Weiner 14.10.2015, 19:55
quelle

2 Antworten

3

Wenn Sie die richtige Version von println finden, wird der Rückgabetyp ignoriert. Siehe JLS 15.13.2 . Es wäre nicht sinnvoll, sie einzubeziehen, da es nicht zwei Versionen einer Methode mit denselben Parametern, sondern einem anderen Rückgabetyp geben kann. Aber jetzt hat der Compiler ein Problem: Sowohl Supplier#get als auch Runnable#run erwarten dieselben Parameter (keine). Also gibt es ein println , das für beide passt. Beachten Sie, dass der Compiler in dieser Phase nur versucht, eine Methode zu finden. Der Compiler läuft grundsätzlich auf das gleiche Problem wie in diesem Code:

%Vor%

println() entspricht Runnable#run und println(String) entspricht Consumer#accept . Wir haben keinen Zieltyp angegeben, daher ist die Situation mehrdeutig.

Nachdem eine Methode ausgewählt wurde, kann der Zieltyp korrekt abgeleitet werden. In dieser Phase ist der Rückgabetyp relevant. Siehe JLS 15.13.2 . Also würde dieser Code natürlich scheitern:

%Vor%

Der Compiler gibt sofort einen Fehler aus, wenn er die Mehrdeutigkeit erkennt. Obwohl ein Fehlerbericht für dieses Verhalten erstellt und akzeptiert wurde, weisen die Kommentare darauf hin, dass Oracle JDK kann sich an die JLS glaubenswerter halten als der EuGH (trotz seines schöneren Verhaltens). Ein späterer Fehlerbericht , der auf der Rückseite dieser SO-Frage angesprochen wurde, wurde wie folgt gelöst: Kein Problem. "Nach einer internen Debatte hat Oracle entschieden, dass das Verhalten von javac korrekt ist.

    
zeroflagL 15.10.2015, 13:08
quelle
2

Es scheint wie ein Fehler in javac Compiler. Das Problem liegt in der überladenen Methode println() . Es hat verschiedene Implementierungen basierend auf dem Typ, den Sie schreiben: int , long , String usw. So Ausdruck: System.out::println hat 10 Methoden zur Auswahl. Eine davon kann zu Runnable , 9 andere zu Consumer<T> abgeleitet werden.

Irgendwie ist der javac -Compiler nicht in der Lage, aus diesem Ausdruck die korrekte Methodenimplementierung abzuleiten. Und wrap(() -> {}) kompiliert korrekt, weil dieser Ausdruck nur eine mögliche Interpretation hat - Runnable .

Ich bin mir nicht sicher, ob solche Ausdrücke unter JLS-Regeln erlaubt sind. Aber folgender Code kompiliert korrekt mit javac (und läuft ohne Laufzeitprobleme):

%Vor%

Es scheint so, als ob dieser Cast dem Compiler die erforderlichen Informationen liefert, um den Typ korrekt abzuleiten, was irgendwie seltsam ist. Ich wusste nicht, dass Cast-Ausdrücke in Typinferenz verwendet werden können.

    
Denis Bazhenov 15.10.2015 02:10
quelle