2014-10-02 38 views
5

Jeśli, na przykład, mieć metodę, która wykorzystuje varargs dla typów klasy, która rozciąga się super klasę jak tym:varargs typu klasy w Javie

public static <E extends Example> void test(Class<E>... es){} 

Potem próbuję wywołanie tej metody z dwoma różnymi podklasy przykładu, mogę to zrobić tylko wtedy, gdy utworzę tablicę z dwiema klasami.

//this does not work 
test(E1.class,E2.class); 
//this does work 
test(new Class[]{E1.class,E2.class}); 
public class E1 extends Example {} 
public class E2 extends Example {} 

Dlaczego tak jest?

Odpowiedz

6

Linia ta nie kompiluje:

test(E1.class,E2.class); 

Jest tylko jeden typ parametru E i Java musi dopasować przypuszczalne typy argumentów dokładnie. Nie można wywnioskować, że są to: Example, ponieważ obiektami są Class<E1> i Class<E2>, a nie Class<Example>. Niezmienność generycznych Java zapobiega temu.

Można obejść ten problem poprzez wprowadzenie górnej granicy wieloznaczny w zakresie ogólnego parametru typu test „s:

public static <E extends Example> void test(Class<? extends E>... es) 

Pozwala Java wywnioskować Example dla E, poprzez spełnienie górna granica wieloznaczny z E1 i E2.

Druga linia tworzy nieprzetworzoną tablicę z Class es, omijając generyczne i generując ostrzeżenie "niezaznaczone połączenie".

new Class[]{E1.class,E2.class} 

Jeśli było próbować dostarczyć typu argumentu Class tutaj, dostaniemy błąd kompilatora z każdej połowie drogi rozsądnym parametru type:

// Needs Class<Example> but found Class<E1> and Class<E2> 
test(new Class<Example>[]{E1.class,E2.class}); 

// Needs Class<E1> but found Class<E2> 
test(new Class<E1>[]{E1.class,E2.class}); 

// Needs Class<E2> but found Class<E1> 
test(new Class<E2>[]{E1.class,E2.class}); 

Zaspokojenie wnioskowania za pomocą wieloznacznych tutaj właśnie odkrywa prawdziwy problem - generyczne tworzenie macierzy.

// Generic array creation 
test(new Class<? extends Example>[]{E1.class,E2.class}); 
+0

Dziękuję bardzo. To nigdy nie było szkodliwe dla mojego programu, ale na pewno drapałem się po nim przez chwilę. Naprawdę doceniam wyjaśnienie. – Squirvin

2

Definiujesz ogólny E pojedynczej klasy, która rozszerza Przykład. Nie możesz odwoływać się do dwóch różnych klas w twoim wywołaniu, ponieważ nie będzie wiedział, jaki typ E jest. Oczekuje tylko jednego typu.

Mimo to nie działa:

test(E1.class, E2.class); 

to robi:

test(E1.class, E1.class); 

Powodem można to zrobić z tablicą z powodu typu skasowaniem. Kompilator nie widzi, że klasy w tablicy są różne.

Po zmianie metody akceptacji dowolnej klasy, która rozszerza Example, będzie działać.

public static void test(Class<? extends Example>...classes) 
+1

Myślę, że wyjaśniłeś to całkiem dobrze. Ale żeby być równym przykładowi OP, twój podpis powinien brzmieć 'public static void test (Class ... classes)'. Będzie jednak (w każdym razie) dać ostrzeżenie '-Xlint: unchecked'. – 5gon12eder

+0

@ 5gon12eder: Podpis podany przez Ciebie i ten w tej odpowiedzi są dokładnie równoważne, tj. Akceptują dokładnie tę samą klasę argumentów. Prostszy (podany w tej odpowiedzi) powinien być zawsze preferowany, ponieważ parametr typu jest niepotrzebny. – newacct

+0

@newacct Przyjmują tę samą klasę argumentów, ale jeśli chcesz odwołać się do typu ogólnego, będziesz potrzebować bardziej złożonej składni. Ponieważ w OP, 'test' zwraca' void' i ma puste ciało, możesz argumentować, że i tak nie chcemy odnosić się do tego typu, ale mówiąc ogólnie, pełna składnia jest również silniejsza. – 5gon12eder