2015-01-22 16 views
7

Podczas aktualizacji aplikacji do Java 8 natknąłem się na dziwny problem z numerem Google Guava newArrayList w kilku miejscach.Java 8 Compiler Confusion Z przeciążonymi metodami

Spójrz na ten przykład:

import com.google.common.collect.UnmodifiableIterator; 

import javax.naming.NamingException; 
import javax.naming.directory.Attribute; 
import javax.naming.directory.BasicAttribute; 
import java.util.ArrayList; 

import static com.google.common.collect.Iterators.forEnumeration; 
import static com.google.common.collect.Lists.newArrayList; 

public class NewArrayListIssue { 
    public static void main(String[] args) throws NamingException { 

     UnmodifiableIterator<?> elements = forEnumeration(getEnumeration().getAll()); 
     System.out.println("declarefirst = " + newArrayList(elements)); // calls newArrayList(Iterator<? extends E> elements) 

     ArrayList directCopy = newArrayList(forEnumeration(getEnumeration().getAll())); 
     System.out.println("useDirectly = " + directCopy); //calls newArrayList(E... elements) 
    } 

    public static Attribute getEnumeration(){ 
     return new BasicAttribute("foo",1); 
    } 
} 

w pierwszym przykładzie, kiedy dostanę UnmodifiableIterator najpierw do własnej zmiennej, a następnie zadzwonić newArrayList dostanę to, czego oczekuję, czyli wartości Iteratory kopiowane do nowy List.

W drugim przykładzie, gdzie forEnumeration idzie bezpośrednio do metody newArrayList, otrzymuję z powrotem List z zawierającym iterator (który zawiera wartość).

Według IntelliJ nim myśli że oba połączenia metody należy do newArrayList(Iterator<? extends E> elements) ale znalazłem podczas debugowania, że ​​druga rozmowa faktycznie idzie do newArrayList(E... elements).

Dzieje się tak tylko wtedy, gdy kompiluję się z Oracle JDK8 ukierunkowanym na Java8. Jeśli celuję na 7, działa dobrze.

Odpowiedz

7

Problem polega na tym, że kompilator uważa, że ​​nie ma zastosowania newArrayList(Iterator<? extends E>) (być może z powodu this bug), a następnie po cichu wybiera ogólną metodę varargs, która ma zawsze zastosowanie (co świadczy o niebezpieczeństwie takiego przeciążenia), gdy nie używasz określonego typu elementu dla listy wyników.

Błąd pojawia się z typów wieloznacznych, czyli w kodzie to Attribute.getAll() Zwracanie NamingEnumeration<?> stąd wynik forEnumeration jest UnmodifiableIterator<?> który kompilator odmawia przypisać Iterable<? extends E>, typ parametru newArrayList. Jeśli rzucisz wartość zwracaną wewnętrznego wywołania na Enumeration, problem zniknie, tak jak podczas przesyłania wartości zwracanej zewnętrznego połączenia do Iterator.

Nie widzę prostego, krótkoterminowego rozwiązania tego problemu. W końcu nie rozumiem, dlaczego nie używałeś List<?> directCopy=Collections.list(getEnumeration().getAll()); w pierwszej kolejności ...

Pamiętaj, że jeśli chcesz znaleźć wszystkie wystąpienia tego problemu, możesz po prostu użyć poprawionej wersji guawy, w której newArrayList(E...) został usunięty i sprawdź wszystkie błędy kompilatora (zakładając, że nie masz wielu przypadków, w których naprawdę chcesz nazwać to przeciążeniem). Po przepisaniu stron z ogłoszeniami możesz wrócić do pierwotnego guawy.

+0

Jest to duży kod z wieloma autorami. Nie mogę więc odpowiedzieć, dlaczego jeden sposób został wybrany na inny. Po prostu muszę je wszystkie znaleźć. Dzięki za podpowiedź na temat łatania guawy. Nie myślałem o tym. Chyba wiem, co robię dziś rano. – ryber

+0

Jedyny problem z usunięciem newArrayList (E ...) polega na tym, że gdy kompilator zacznie używać właściwego. Wygląda na to, że jedyną bezpieczną rzeczą jest szybkie sprawdzenie ich wszystkich. – ryber

+1

To dziwne, ponieważ powodem, dla którego nie należy używać właściwego, jest to, że kompilator uważa, że ​​właściwy nie ma zastosowania i że nie powinien nagle zmienić się, gdy niewłaściwy został usunięty. Kiedy próbowałem sztuczek z Netbeanami, działało to zgodnie z oczekiwaniami, ale może być inaczej w przypadku IntelliJ, które, jak powiedziałeś, wybrałoby właściwy cel wywołania. Ale pomyślałem, że jeśli IntelliJ i leżący u podstaw javac nie zgadzają się, IntelliJ nie ukryje błędów javac. Czy rzeczywiście generuje pliki klas? – Holger

1

Widziałem to z przeciążonymi metodami i typami ogólnymi. W tym przypadku wybiera się bardziej ogólną wersję newArrayList(), gdy parametr nie jest jawnie wpisany.

nie mam technicznego wyjaśnienia dla ciebie, ale polecam wymusić korzystanie z żądanym przez przeciążenie metody rzucania:

ArrayList directCopy = newArrayList((Iterator)forEnumeration(getEnumeration().getAll())); 
+0

Ah ale to oznacza, że ​​muszę je wszystkie znaleźć pierwszy! – ryber

+0

Hehe bummer:/To powinno być po prostu "znaleźć wszystkie odniesienia", ponieważ IntelliJ poprawnie określa to prawo? – gknicker

+0

Pomyślałem, ale Intellij jest dziwny i daje mi wszystkie wariacje z powrotem. Plus newArrayList (Iterator jest dość powszechny, ponieważ często jest używany w połączeniu z filtrem i innymi.Więc muszę przejść przez wszystkie i znaleźć te z niejednoznacznymi rodzajami (na 600 lat). – ryber