2015-11-21 54 views
7

Uwaga: Znalazłem wiele pytań wskazujących różnice między javac i kompilatorem Eclipse, ale o ile mogłem zobaczyć, wszystkie z nich omawiają inne problemy.Generics i lambdas - różne zachowania w javacu i kompilatorze Eclipse

Załóżmy, że mamy tę metodę:

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

znalazłem różne zachowanie między javac i kompilator Eclipse Java przy zestawianiu połączeń do tej metody i nie jestem pewien, który z nich ma rację.


proste zastosowanie tej metody może być:

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

Kompilator powinny być zdolne do wiązania T do Optional<String> pomocą pierwszego argumentu i U do String pomocą drugiego. Więc to wezwanie powinno być ważne (moim zdaniem).

To kompiluje grzywny za pomocą javac ale nie kompilacji przy użyciu Eclipse:

Niezgodność typów: nie można przekonwertować z pustki do <nieznanego>

Dodanie typu argumentu pierwszego argumentu (() -> Optional.<String> of("foo")) sprawia, że ​​kompiluje się również w Eclipse.

Pytanie: Z punktu widzenia specyfikacji, czy Eclipse jest prawidłowy w odrzucaniu tego połączenia (i dlaczego (nie))?


Załóżmy teraz chcemy rzucać niestandardowy (wykonywania) wyjątek, jeśli Optional jest pusty:

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

ten zostaje odrzucony zarówno przez javac i kompilator Eclipse, ale z różnych komunikatów o błędach :

  • javac „nieudokumentowanych wyjątkiem X, musi być złapany lub uznane za wyrzucane”
  • Eclipse kompilator: „Niezgodność typów: nie można przekonwertować z pustki do <nieznany>”

Kiedy dodać typ argumentu pierwszego argumentu, jak wyżej, Eclipse uda się kompilacja natomiast javac nadal kończy się niepowodzeniem. Kiedy dodaję <RuntimeException> jako typ argumentu do drugiego argumentu, jest odwrotnie, Eclipse nie powiedzie się i javac się powiedzie.

Pytanie: Znowu, czy kompilatory mają rację, odrzucając to połączenie i dlaczego?


Moim zdaniem oba warianty powinny kompilować dobrze bez dodatkowych wskazówek przy użyciu argumentów typu. Jeśli tak, wypełnię jeden raport o błędzie dla javac (dotyczący "niezgłoszonego wyjątku") i jeden dla kompilatora Eclipse (dotyczący "niezgodności typów"). Ale najpierw chcę mieć pewność, że specyfikacja podziela mój punkt widzenia.

wersje stosowane:

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

Edycja:

Napełniłem bug 482781 dla problemu w Eclipse.

Problem z numerem javac jest już zgłoszony jako JDK-8056983, patrz Tunakis answer.

+1

Obwiniam zaćmienie w razie wątpliwości :) Wnioskowanie jest bardzo skomplikowane, cała składnia lambda jest wciąż całkiem nowa. Eclipse ma kilka błędów naprawionych, ale niektóre pozostały w obecnej wersji, związane z takimi przypadkami. – zapl

+1

Kompilator Eclipse Mars ECJ jest bardzo wadliwy w porównaniu do najnowszej wersji Luny, jeśli chodzi o ogólną ekspansję. Już potknąłem się o co najmniej trzy przypadki, gdy ECJ 3.11 zawiedzie lub nawet utknął w nieskończonej pętli, podczas gdy javac i ECJ 3.10 poprawnie się kompilują. Dlatego wciąż używam Luny. –

+0

Błąd Eclipse został już naprawiony na 4.6 M1 przez https://bugs.eclipse.org/470826, który jest również zaplanowany na port z powrotem na mars.2. –

Odpowiedz

5

Tak, masz rację pod każdym względem. Szczerze mówiąc nie mógłbym łączyć się z określonymi liniami JLS na ten temat: wpisz propozycję is a whole chapter.

Zastrzeżenie: testowałem przy użyciu Eclipse Mars 4.5.1 i JDK 1.8.0_60.


Wariant 1 powinien skompilować i Eclipse ma błąd tutaj. Nie mogłem znaleźć nic z tego związanego w Bugzilli, więc mógłbyś go napisać dalej. można zapewnić sobie, że powinien skompilować jeśli ograniczyć się do tego przykładu:

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

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

To kompiluje dobrze zarówno z Eclipse i javac. Dodanie parametrów nie powinno zmienić typu wywnioskowanego dla T podczas kompilacji.


Wariant 2 nie kompiluje dla javac i jest to rzeczywiście bug, jak podano w JDK-8056983. Kompilator powinien być w stanie wywnioskować, że X to . Jeśli chodzi o to, dlaczego Eclipse nadal nie jest w stanie tego skompilować, znowu nie mogłem znaleźć niczego w Bugzilli, więc możesz to zgłosić!

+1

Poprawka dla https://bugs.eclipse.org/470826 pozwala Eclipse akceptować oba warianty. –