2015-11-21 6 views
7

Hinweis: Ich habe mehrere Fragen gefunden, die Unterschiede zwischen javac und dem Eclipse-Compiler zeigen, aber soweit ich sehen konnte, diskutieren alle anderen Probleme.Generics und Lambdas - unterschiedliches Verhalten in Javac und Eclipse Compiler

Angenommen, wir haben diese Methode:

public static <T, U> void foo(Supplier<T> a, Function<T, U> b, Consumer<U> c) 
{ 
    c.accept(b.apply(a.get())); 
} 

ich unterschiedliches Verhalten zwischen javac und die Eclipse-Java-Compiler gefunden, wenn Anrufe an diese Methode kompilieren und ich bin mir nicht sicher, wer von beiden recht hat.


Eine einfache Anwendung dieses Verfahrens könnte sein:

// variant 1 
foo(
    () -> Optional.of("foo"), 
    value -> value.get(), 
    value -> System.out.println(value)); 

Der Compiler der Lage sein sollte zu TOptional<String> unter Verwendung des ersten Arguments und U zu String unter Verwendung des zweiten zu binden. Also sollte dieser Anruf (meiner Meinung nach) gültig sein.

Dies kompiliert feinen javac verwenden, aber nicht Eclipse kompilieren mit:

Typenkonflikt: kann nicht von Leere konvertieren <unbekannt>

eine Art Argument auf das erste Argument Hinzufügen (() -> Optional.<String> of("foo")) macht es auch in Eclipse kompilieren.

Frage: Aus Sicht der Spezifikation ist Eclipse korrekt in der Ablehnung dieses Aufrufs (und warum (nicht))?


Jetzt nehmen wir eine benutzerdefinierte (Runtime) Ausnahme werfen wollen, wenn die Optional leer ist:

// variant 2 
foo(
    () -> Optional.of("foo"), 
    value -> value.orElseThrow(() -> new RuntimeException()), 
    value -> System.out.println(value)); 

Dies wird durch beide abgelehnt, javac und der Eclipse-Compiler, aber mit verschiedenen Fehlermeldungen

  • javac: "nicht gemeldete Ausnahme X, müssen gefangen oder deklariert werden geworfen werden"
  • Eclipse-Compiler:

: „Typenkonflikt kann nicht von Leere zu <unbekannt> konvertieren“ Wenn ich den Typ Argument auf das erste Argument hinzufügen, wie oben Eclipse erfolgreich bei der Erstellung während javac immer noch nicht. Wenn ich <RuntimeException> als Typargument zu dem zweiten Argument hinzufüge, ist es umgekehrt, Eclipse schlägt fehl und javac ist erfolgreich.

Frage: Noch einmal, haben die Compiler Recht, diesen Aufruf abzulehnen und warum?

Meiner Meinung nach sollten beide Varianten gut ohne zusätzliche Hinweise kompilieren, indem Sie type Argumente verwenden. Wenn das der Fall ist, fülle ich einen Fehlerbericht für javac (in Bezug auf die "nicht gemeldete Ausnahme") und einen für den Eclipse-Compiler (in Bezug auf den "Typenkonflikt"). Aber zuerst möchte ich sicher sein, dass die Spezifikation meine Sichtweise teilt.

verwendet Versionen:

  • javac: 1.8.0_66
  • Eclipse-JDT: 3.11.1.v20151118-1100

EDIT:

I gefüllt bug 482781 für das Problem in Eclipse. Das Problem mit javac ist bereits gemeldet als JDK-8056983, siehe Tunakis answer.

+1

Wäre Schuld im Zweifel Eklipse :) Typ Inferenz ist sehr kompliziert, die ganze Lambda-Syntax ist noch ziemlich neu. Eclipse hat einige Bugs behoben, aber einige sind in der aktuellen Version übrig geblieben. – zapl

+1

Der Eclipse Mars ECJ Compiler ist im Vergleich zum neuesten Luna wirklich buggy, wenn es um generische Erweiterungen geht. Ich bin schon mit mindestens drei Fällen gestolpert, als ECJ 3.11 fehlschlägt oder sogar in der Endlosschleife feststeckt, während javac und ECJ 3.10 richtig kompilieren. Deshalb benutze ich immer noch Luna. –

+0

Der Eclipse-Fehler wurde bereits für 4.6 M1 über https://bugs.eclipse.org/470826 behoben, der auch für den Backport auf mars.2 vorgesehen ist. –

Antwort

5

Ja, Sie sind in jeder Hinsicht richtig. Ich wäre ehrlich gesagt nicht in der Lage, mich mit bestimmten Zeilen der JLS zu verbinden: type inference is a whole chapter.

Haftungsausschluss: Ich habe mit Eclipse Mars 4.5.1 und JDK 1.8.0_60 getestet.


Variante sollte 1 kompilieren und Eclipse hat hier einen Fehler. Ich konnte nichts in ihrem Bugzilla finden, also könntest du weitermachen und es ablegen. Sie können sich versichern, dass es kompilieren sollten, wenn Sie Ihr Beispiel dazu reduzieren:

public static <T> void foo(Supplier<T> a) { 
    a.get(); 
} 

foo(() -> Optional.of("foo")); 

Dies sowohl mit Eclipse und javac fein kompiliert. Das Hinzufügen von Parametern ändert (sollte) nicht den Typ, der während der Kompilierung für T abgeleitet wird.


Variante 2 lässt sich nicht kompilieren für javac und dies ist in der Tat ein Fehler, wie in JDK-8056983 berichtet. Der Compiler sollte in der Lage sein zu folgern, dass XRuntimeException ist. Warum Eclipse das immer noch nicht kompilieren kann, konnte ich in seinem Bugzilla nichts finden.

+1

Das Update für https://bugs.eclipse.org/470826 lässt Eclipse beide Varianten akzeptieren. –

Verwandte Themen