2010-06-17 9 views
47

Tak, mam klasy z konstruktora tak:Dlaczego użycie Collections.emptySet() z generics działa w przydziale, ale nie jako parametr metody?

public FilterList(Set<Integer> labels) { 
    ... 
} 

i chcę zbudować nową FilterList obiekt z pustym zestawem. Po porady Joshua Blocha w jego książce Efektywna Java, nie chcę tworzyć nowego obiektu dla pustego zestawu; Ja po prostu użyć Collections.emptySet() zamiast:

FilterList emptyList = new FilterList(Collections.emptySet()); 

To daje mi błąd, twierdząc, że nie jest java.util.Set<java.lang.Object>java.util.Set<java.lang.Integer>. OK, a co powiesz na to:

FilterList emptyList = new FilterList((Set<Integer>)Collections.emptySet()); 

Daje mi to również błąd! OK, a co powiesz na to:

Set<Integer> empty = Collections.emptySet(); 
FilterList emptyList = new FilterList(empty); 

Hej, działa! Ale dlaczego? W końcu Java nie ma typu interferencji, dlatego pojawia się niezaznaczone ostrzeżenie o konwersji, jeśli wykonasz Set<Integer> foo = new TreeSet() zamiast Set<Integer> foo = new TreeSet<Integer>(). Ale Set<Integer> empty = Collections.emptySet(); działa bez ostrzeżenia. Dlaczego?

+2

wszystkie poniższe odpowiedzi są poprawne, ale to, czego nie rozumiem: dlaczego prawda zainicjować swoją kolekcję za pomocą emptyList zamiast wywoływania domyślnego konstruktora bez parametrów? Każda znana kolekcja ma konstruktor z istniejącą kolekcją i pustym konstruktorem. –

+0

Co ciekawe, kompilacja DOES: 'FilterList emptyList = new FilterList ((Set ) (Set ) Collections.emptySet());' – EricS

+1

Kolejny całkiem zabawny przykład: 'Set emptySet = (Set ) Collections.emptySet(); 'nie kompiluje się. – neo

Odpowiedz

106

Krótka odpowiedź brzmi - jest to ograniczenie wnioskowania o typ w systemie generycznym języka Java. Potrafi wnioskować typy rodzajowe w odniesieniu do konkretnych zmiennych, ale nie od parametrów metod.

Podejrzewam to dlatego, że metody są wywoływane dynamicznie w zależności od klasy wykonawczego obiektu będącego właścicielem, więc w czasie kompilacji (jeśli wszystko ogólne informacje zostanie rozwiązany) można faktycznie nie wiem na pewno, co w klasie parametru metody będzie, a zatem nie może wnioskować. Deklaracje zmiennych są ładne i stałe, więc możesz.

Ktoś inny może podać więcej szczegółów i/lub ładny link. :-)

W każdym razie, zawsze można określić parametry dla połączeń typu jawnie generycznych tak:

Collections.<Integer>emptySet(); 

lub nawet kilku parametrów jednocześnie, na przykład

Collections.<String, Boolean>emptyMap(); // Returns a Map<String, Boolean> 

Często wygląda to trochę czystsze niż rzucanie, w przypadkach, w których nie dochodzi do konkluzji.

+1

+1 dla dobrego wyjaśnienia, dlaczego. Chciałbym zaznaczyć dwie poprawne odpowiedzi w języku SO :) – jdmichal

+3

Wiem, że kompilator nie będzie szukał wywołania metody dla kontekstu podczas wnioskowania, ale nie jestem pewien dlaczego. Przynajmniej w przypadku metod prywatnych lub "ostatecznych", powinno być możliwe przeprowadzenie inferencji. –

+2

@Hank: lub 'statyczne' metody, które są rozwiązywane również w czasie kompilacji. Nadal - to * nie *, co chyba jest głównym punktem. –

5

Chcesz to zrobić:

FilterList emptyList = new FilterList(java.util.Collections.<Integer>emptySet()); 

, który mówi, że metoda emptySet jej nazwę rodzajową parametr powinien wyraźnie przez Integer zamiast domyślnego Object. I tak, składnia jest całkowicie funky i nieintuicyjna. :)

+1

Nie byłem świadom tego synteksu, dzięki! Ale wciąż zastanawiam się, dlaczego ta składnia nie jest konieczna w przypisaniu zmiennych. –

+0

Zobacz odpowiedź Andrzeja Doyle'a. Uważam, że jest to dobre wytłumaczenie. – jdmichal

+0

Nie potrzebujesz "nowego" tam. W rzeczywistości nie sądzę, że się z tym skompiluje. –

6

spróbować

FilterList emptyList = new FilterList(Collections.<Integer>emptySet()); 

Można wymusić parametr typu dla metod, które je mają, w przypadkach, gdy wniosek nie jest wystarczająco dobre, lub pozwalają korzystać podtypy; na przykład:

// forces use of ArrayList as parameter instead of the infered List 
List<String> l = someObject.<ArrayList<String> methodThatTakesTypeParamForReturnType(); 
Powiązane problemy