2009-10-03 14 views
5

Mam interfejs A, który implementuje narzędzia klasy B.Java Generics Curiosity

Poniższa metoda rodzajowa działa

public static <T, U extends T> List<T> listFactory(Collection<U> source) { 
    return new ArrayList<T>(source); 
} 

ale

public static <T> List<T> listFactory(Collection<? extends T> source) { 
    return new ArrayList<T>(source); 
} 

nie (błąd kompilacji typu niedopasowanie), kiedy jestem kierowania wyjście do

List<A> tester = listFactory(B.defaultCollectionFactory(3)); 

defaultCollectionFactory(int count) statycznie zapewnia kolekcję B s, z domyślny schemat etykietowania.

Wszelkie spostrzeżenia na temat tego, dlaczego tak jest? Wygląda na to, że ogólny U i symbol wieloznaczny robią to samo.

+1

Jak to działa "nie działa"? jaki jest komunikat o błędzie? – newacct

+1

Wygląda na to, że masz dwie prawidłowe odpowiedzi poniżej, co jest przyczyną problemu - ale nie wziąłbym ich rady dosłownie. Trzymaj się wersji niezabezpieczonej, aby wywołanie metody nie musiało obejść tego ograniczenia symboli wieloznacznych. –

+0

@newacct: błąd kompilacji, niedopasowanie typu – Carl

Odpowiedz

2

W pierwszym konstrukcie określasz, że zwracasz List interfejsu elementu, który został przekazany. Określasz relację między przekazanym obiektem i zwracanym typem obiektu w kierunku U extends T. W tym przypadku kompilator może skojarzyć odpowiednio A i B zi U.

W drugim nie ma takiego rozróżnienia, więc kompilator przyjmuje, że T odnosi się do B i wpisz wartość zwracaną jako List<B>. Następnie wpadasz w pułapkę, gdzie, chociaż B jest instancją A, List<B> nie jest instancją List<A>. Kompilator będzie narzekać:

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

znajdziesz, że z pierwszym konstruktem, masz swobodę określania List dowolnego interfejsu narzędzia lub dowolnej superklasy w hierarchii B (na przykład), a kompilator nie będzie narzekał.

+0

Zatem pierwszeństwo (rodzaj) dla wnioskowania typu pochodzi najpierw od argumentów, a następnie typ zwracany dla tego rodzaju generycznych. – Carl

+0

wybrana w oparciu o dobre wyjaśnienie (które oferuje również erickson), a także unikanie doradzania klasie. struktura, która moim zdaniem jest brzydka. – Carl

3

Kompilator wywnioskuje inny typ parametru dla metody listFactory niż oczekiwano. Podaje, że T jest typu B, więc podpis jest efektywnie List<B> listFactory(Collection<? extends B> source). Określ parametr typu A, jawnie wprowadzając wywołanie metody:

List<A> tester = Test.<A> listFactory(B.defaultCollectionFactory(3)); 
+0

@erikson: To statyczne wywołanie sparametryzowanego typu faktycznie działa, nie ma dla mnie sensu, czy można je rozbić za pomocą przykładu i wyjaśnienia teorii, dzięki. –

+0

Każda statyczna metoda może (naprawdę powinna być) zakwalifikowana przez klasę, do której należy. PO nie precyzował, co to jest; Użyłem "Test". W ten sposób uzyskasz normalne wywołanie metody statycznej: 'Test.listFactory (...)'. Następnie argumenty typu dla dowolnej sparametryzowanej metody można jawnie określić, dodając je przed nazwą metody. To jest ''. Połącz je i otrzymasz moją odpowiedź. – erickson