2013-07-11 15 views
5

Supponiamo che ci sia il seguente codice:Perché il casting implicito funziona mentre il casting di reflection genera un'eccezione?

@SuppressWarnings("unchecked") 
public static <T> T implicitCaster(Class<T> cls, Object o) { 
    return (T) o; 
} 

public static <T> T reflectionCaster(Class<T> cls, Object o) { 
    return cls.cast(o); 
} 

Il codice funziona come previsto in entrambi i casi con la seguente eccezione, trovato in primitive:

public static void main(String[] args) { 
    System.out.println(implicitCaster(int.class, 42)); 
    System.out.println(reflectionCaster(int.class, 42)); 
} 

La prima chiamata funziona come previsto, ma la seconda chiamata genera java.lang.ClassCastException.

Si tratta di un caso in cui è stato ignorato l'autoboxing? O è impossibile fornire autoboxing in questo caso, di reflection casting? Oppure c'è qualcos'altro che causa questa incoerenza?

Edit: chiamando questo codice funziona come previsto:

public static void main(String[] args) { 
    System.out.println(implicitCaster(Integer.class, 42)); 
    System.out.println(reflectionCaster(Integer.class, 42)); 
} 

risposta

3

Questo accade a causa della cancellazione di tipo.

In fase di runtime, i parametri di tipo generico non esistono.
La trasmissione di un oggetto a un parametro di tipo generico non ha alcun effetto. (che è il motivo per cui viene visualizzato l'avviso di cast non selezionato)

Pertanto, le caselle di prima linea da 42 a Object devono passare al metodo.
La funzione restituisce quindi Object, passata a System.out.println.


tua seconda chiamata chiama il metodo del tipo primitivo intcast.
Ciò genera un'eccezione, poiché gli oggetti non possono essere convertiti in tipi primitivi. (auto-boxing è una funzionalità puramente in fase di compilazione, quindi non aiuta)

L'errore si verifica quando cast()checks isInstance() per verificare che il cast sia valido.

I documenti per isInstance() say:

In particolare, se questo oggetto Class rappresenta una classe dichiarata, questo metodo restituisce vero se l'argomento oggetto specificato è un'istanza della classe rappresentata (o di una delle sue sottoclassi) ; restituisce falso altrimenti. Se questo oggetto Class rappresenta una classe array, questo metodo restituisce true se l'argomento Object specificato può essere convertito in un oggetto della classe array mediante una conversione dell'identità o una conversione di riferimento ampliamento; restituisce falso altrimenti. Se questo oggetto Class rappresenta un'interfaccia, questo metodo restituisce true se la classe o qualsiasi superclasse dell'oggetto Argomento specificato implementa questa interfaccia; restituisce falso altrimenti. Se questo oggetto di classe rappresenta un tipo primitivo, questo metodo restituisce false.

(enfasi aggiunta)


La modifica funziona perché non è più in uso un tipo primitivo.
In entrambi i casi, il compilatore autoboxes 42 in modo che possa essere passato come un oggetto.

La prima chiamata, come prima, non ha alcun effetto.
La seconda chiamata verifica che il numero intero in scatola sia di fatto un'istanza della classe Integer, quindi lo restituisce.

+0

Ho scritto lì che il codice funziona per ogni classe data con l'eccezione dei primitivi; quindi è possibile trasmettere un oggetto a un tipo generico. In secondo luogo puoi lanciare oggetti ai primitivi. – m3th0dman

+0

@ m3th0dman: non ho detto che è impossibile. Ho detto che non ha alcun effetto in fase di runtime. – SLaks

+0

Capisco ora; ma questo tipo di rompere il contratto del metodo cast non lo fa? Tecnicamente il cast è possibile. – m3th0dman