2014-10-02 16 views
5

Se io, per esempio, ho un metodo che utilizza varargs per i tipi di classe che estende una classe di super come questo:varargs di tipo classe in Java

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

Allora provo a chiamare quel metodo con due diversi sottoclassi di Example, posso farlo solo se creo un array con le due classi in esso contenute.

//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 {} 

Perché è questo?

risposta

6

Questa linea non può essere compilato:

test(E1.class,E2.class); 

C'è un solo tipo di parametro E, e Java deve corrispondere ai tipi dedotto di argomenti esattamente. Non può dedurre Example, perché gli oggetti sono Class<E1> e Class<E2>, non Class<Example>. L'invarianza dei generici Java impedisce che ciò accada.

È possibile aggirare il problema introducendo un jolly limite superiore parametro di tipo generico test s':

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

Questo permette Java per inferire Example per E, soddisfacendo il carattere jolly limite superiore con E1 e E2.

La seconda riga crea una matrice raw di Class es, ignorando i generici e generando un avviso di "chiamata non verificata".

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

Se si dovesse tentare di fornire un argomento di tipo a Class qui, si otterrebbe un errore di compilazione con qualsiasi parametro di tipo ragionevole a metà strada:

// 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}); 

Soddisfare l'inferenza utilizzando un carattere jolly qui scopre solo il vero problema qui: la creazione di array generici.

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

Grazie mille. Non è mai stato dannoso per il mio programma, ma mi sono sicuramente grattato la testa per un po '. Apprezzo molto il chiarimento. – Squirvin

2

Si definisce la E generica di una singola classe che estende l'esempio. Non puoi fare riferimento a due classi diverse nella tua chiamata perché non saprà quale sia il tipo E. Si aspetta solo un tipo.

Anche se questo non funziona:

test(E1.class, E2.class); 

Questo fa:

test(E1.class, E1.class); 

Il motivo lo si può fare con una serie è a causa del tipo di cancellazione. Il compilatore non vede che le classi all'interno della matrice sono diverse.

Se si modifica il metodo per accettare qualsiasi classe che si estende Example funzionerà.

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

Penso che lo abbiate spiegato abbastanza bene. Ma per essere uguale all'esempio dell'OP, la tua firma dovrebbe essere 'public static void test (classe ...)'. Comunque (darà comunque un avvertimento '-Xlint: unchecked', comunque. – 5gon12eder

+0

@ 5gon12eder: la firma che hai dato e quella in questa risposta sono esattamente equivalenti, cioè accettano la stessa classe di argomenti. Il più semplice (quello dato in questa risposta) dovrebbe sempre essere preferito perché il parametro type non è necessario. – newacct

+0

@newacct Accettano la stessa classe di argomenti, ma se vuoi fare riferimento al tipo generico, avrai bisogno della sintassi più dettagliata. Dato che nell'OP, 'test' restituisce' void' e ha un corpo vuoto, si potrebbe obiettare che non vogliamo comunque fare riferimento al tipo, ma parlando in generale, la sintassi verbosa è anche più potente. – 5gon12eder

Problemi correlati