2012-01-05 10 views
5

Eclipse afferma che l'operazione instanceof non è consentita con Type Parameter a causa del tipo generico gomma.Perché "t instanceof T" non è ammesso dove T è un parametro di tipo e t è una variabile?

Sono d'accordo che in fase di runtime, nessun tipo di informazione rimane. Ma si consideri la seguente dichiarazione generica di classe:

class SomeClass<T>{ 
    T t; 
    SomeClass(Object o){ 
     System.out.println(o instanceof T); // Illegal 
    } 
}   

In fase di esecuzione, senza T sarebbe stato presente! Ma se istanzio questa classe di tipo Integer, allora l'oggetto corrispondente avrà un campo t di tipo Integer.

Quindi, perché non riesco a controllare il tipo di una variabile con T che può essere sostituita da Integer in fase di esecuzione. E in realtà farei qualcosa come "o instanceof Integer".

In quali casi, consentire l'istanza di un parametro di tipo può causare problemi in modo che sia vietato?

+7

Hai già detto "Al momento dell'esecuzione, non sarebbe presente T", quindi sembra che tu sia già a conoscenza della cancellazione di tipo * *. Quindi non sono sicuro del motivo per cui sei confuso da questo comportamento? –

+1

Solo perché non consentito non significa che può causare il rublo. T è un tipo generico e può infatti essere qualsiasi cosa, usando l'istanza di appena fatto ha senso. – Peter

+0

possibile duplicato di [Java: Instanceof e Generics] (http://stackoverflow.com/questions/1570073/java-instanceof-and-generics) – millimoose

risposta

3

Dopo aver compilato la dichiarazione o instanceof T sarebbe o instanceof Object e poiché tutti i tipi derivano da Oggetto, verrà sempre valutata true. Consentire questo tipo di test darebbe risultati falsi positivi

0

Gli argomenti di tipo generico non sono noti in fase di esecuzione, quindi non esiste una classe con cui è possibile confrontare. T è noto solo al momento della compilazione. I generici aiutano solo lo sviluppatore a scrivere codice più facilmente. Ma al momento dell'esecuzione, gli argomenti sono solo istanze Object.

4

Se è necessario T in fase di esecuzione, è necessario fornirlo in fase di esecuzione. Questo è spesso fatto passando la Classe < T> quale T deve essere.

class SomeClass<T> { 
    final T t; 

    public SomeClass(Class<T> tClass, T t) { 
     if(!tClass.isAssignableFrom(t.getClass()) throw new IllegalArgumentException("Must be a " + tClass); 
     this.t = t; 
    } 

    private SomeClass(T t) { 
     this.t = t; 
    } 

    public static <T> SomeClass<T> of(Class<T> tClass, T t) { 
     if(!tClass.isAssignableFrom(t.getClass()) throw new IllegalArgumentException("Must be a " + tClass); 
     return new SomeClass(t); 
    } 
} 

// doesn't compile 
SomeClass<Integer> intSomeClass = SomeClass.of(Integer.class, "one"); 

Class clazz = Integer.class; 
// compiles with a warning and throws an IAE at runtime. 
SomeClass<Integer> intSomeClass = (SomeClass<Integer>) SomeClass.of(clazz, "one"); 

// compiles and runs ok. 
SomeClass<Integer> intSomeClass = SomeClass.of(Integer.class, 1); 
3

A causa di type erasure, questo non funziona mai. Al runtime, si sa solo che la classe ha un parametro di tipo T, ma non quale tipo è per una determinata istanza. Quindi non è possibile determinare se un oggetto è di tipo T per cominciare, perché non si sa cosa sia T, non perché causerebbe qualche tipo di problema.

Se avete bisogno di fare questo tipo di controllo del tempo di esecuzione, passare un tipo di token per l'oggetto in modo esplicito:

SomeClass(Object o, Class<T> type) { 
    System.out.println(type.isInstance(o)); 
} 
1

Ma se un'istanza di questa classe di tipo Integer, poi l'oggetto corrispondente avrà un campo t di tipo Integer.

In realtà, non lo sarebbe. Avrebbe un campo t di tipo Object. Come hai detto, i generici sono quasi interamente zucchero sintattico (l'eccezione è che quando si estende una classe generica e si specifica un parametro di tipo, il tipo rimane come metadati nel file di classe).

6

Ma se un'istanza di questa classe di tipo Integer, poi l'oggetto corrispondente avrà una t campo di tipo Integer

No, non lo farà. Avrà un campo di tipo Object. Ogni volta che accedi, verrà eseguito il cast su un numero intero.

Si consideri il seguente codice:

SomeClass<Integer> c = new SomeClass<Integer>(); 
SomeClass untyped = (SomeClass)c; // Which type was it? 
SomeClass<String> stringTyped = (SomeClass<String>)untyped; // Now it's STRING?? 

Works. Ti dà un sacco di avvisi del compilatore, ma funziona. Perché il campo T è in realtà di tipo Object e può essere lanciato su qualsiasi cosa.

0

Java realizzare i suoi Generics usando "cancellazione", si può controllare il tipo e cancellare le informazioni sui parametri di tipo in "tempo COMPILE", in "RUN TIME" Ci saranno solo i CONFINI dei parametri di tipo , quindi non ci sarà nulla come "Integer"