2012-06-22 18 views
8

Immaginate il seguente scenario:Riflesso per Classe di parametro generico in Java?

class MyClass extends OtherClass<String>{ 

    String myName; 
    //Whatever 

} 

class OtherClass<T> { 

    T myfield; 

} 

E sto analizzando MiaClasse utilizzando la riflessione specificamente (MyClass.class).getDeclaredFields(), in questo caso mi metterò i seguenti campi (e tipi, utilizzando getType() del campo):

myName --> String 
myField --> T 

Voglio ottenere il tipo effettivo per T, che è noto al runtime a causa dell'esplicita "String" nella notazione extends, come posso ottenere il tipo non genetico di myField?

EDIT DELIBERATO:

Sembra che la risposta è "non si può". Per coloro che potrebbero esaminare questa domanda in un secondo momento, raccomanderei l'utilizzo di Jackson (stavo cercando di farlo per generare JSON) e annotando le classi e i campi in modo tale che Jackson sia a conoscenza della gerarchia dell'ereditarietà e possa fare automaticamente ciò che la risposta corretta sotto suggerita.

+1

La risposta non è che non puoi; puoi facilmente creare una mappa da parametri di tipo a argomenti di tipo esplicito. – Jeffrey

risposta

18

Questo può essere ottenuto con la riflessione solo perché è stato utilizzato esplicitamente String, altrimenti queste informazioni sarebbero andate perse a causa della cancellazione del tipo.

ParameterizedType t = (ParameterizedType) MyClass.class.getGenericSuperclass(); // OtherClass<String> 
Class<?> clazz = (Class<?>) t.getActualTypeArguments()[0]; // Class<String> 
+0

Sembra che tu abbia vinto la competizione per la risposta completa :) – Jochen

+0

Questa è un'ottima risposta, tuttavia come posso farlo in generale con un oggetto Field? Non voglio chiedere esplicitamente la super classe perché sto solo cercando di ottenere tutti i campi dichiarati della mia sottoclasse, uno dei quali è il generico. Voglio solo qualcosa di simile a getDeclaredFields(), ma più intelligente sui generici. Ha senso? –

+0

@hatboysam In un modo o nell'altro devi chiedere la super classe. Chiamando 'Field.getGenericParameter' restituirò sempre solo il parametro type (in questo caso' T'), non l'argomento type esplicito ('String'). Puoi prendere questo parametro di tipo e trovare l'argomento di tipo esplicito creando una mappa da 'MyClass.class.getSuperclass(). GetTypeArguments()' a 'MyClass.class.getGenericSuperclass(). GetActualTypeArguments()'. – Jeffrey

-4

Non esiste un modo diretto per ottenere il tipo effettivo a causa dello Type Erasure. Tuttavia è possibile utilizzare il seguente modo:

nel vostro OtherClass<T>, scrivere il seguente metodo astratto:

protected abstract class<T> getClazz(); 

Poi, nel MyClass, si implementa il metodo:

@Override 
protected Class<String> getClazz(){ 
    return String.class; 
} 

quindi è possibile chiamare getClazz() per ottenere la classe.

+0

+1 per il collegamento a Type Erasure – BlackVegetable

+2

L'OP ha fornito un parametro di tipo concreto per la sua classe generica, che l'informazione * è * mantenuta in fase di esecuzione e può essere recuperata. – Jeffrey

+0

-1 - Questa soluzione non è necessaria come gli altri indicati su questo post.Inoltre, il tuo 'getClass()' avrebbe un nome in conflitto con il metodo finale ['Object # getClass()'] (http://docs.oracle.com/javase/7/docs/api/java/lang/Object .html # getClass \ (\)) –

0

I tipi generici sono non noti in fase di esecuzione. Solo il compilatore sa di loro, controlla che il tuo programma sia stato digitato correttamente e quindi li rimuove.

Nel tuo caso particolare, chiamare MyClass.class.getGenericSuperclass() potrebbe darti le informazioni che ti servono, perché per qualche strana ragione, i tipi concreti usati quando si ereditano sono mantenuti nel descrittore di classe.

+2

Non è "qualche strana ragione". * Le dichiarazioni * di campi, metodi, superclassi, classi di inclusione, ecc. Vengono sempre mantenute, perché le altre classi devono usarle durante la compilazione. Questo è un problema separato dai tipi di oggetti di runtime, dove non ci sono generici. – newacct

3

ho trovato una bella spiegazione here:

Quando runtime ispezione di un tipo parametrizzabile stessa, come java.util.List, non c'è modo di sapere che tipo è è stata parametrizzata per. Questo ha senso poiché il tipo può essere parametrizzato a tutti i tipi di tipi nella stessa applicazione. Tuttavia, quando si ispeziona il metodo o il campo che dichiara l'uso di un tipo parametrizzato, è possibile vedere in fase di esecuzione su che tipo è stato parametrizzato il tipo parametrizzabile.

In breve:

Non è possibile vedere su un tipo di per sé che tipo è parametrizzato ad un runtime, ma si può vedere nei campi e metodi in cui viene utilizzato e parametrizzato.

in codice:

Non si vede T qui:

class MyClass<T> { T myField; } 

Si può vedere il "T" qui:

class FooClass { 
    MyClass<? extends Serializable> fooField; 
} 

Qui si sarebbe in grado di dire la digitare e digitare i parametri di fooField. Vedere i metodi getGeneric*() di Class e Method.

A proposito, vedo spesso questo (ridotto):

Class fieldArgClass = (Class) aType.getActualTypeArguments()[0]; 

Questo non è corretto, perché getActualTypeArguments() può, e spesso ne, tornare TypeVariable posto di classe - in quel momento che il generico è <? extends SomeClass> invece di solo <SomeClass>. Si può andare più in profondità, immaginare:

class FooClass { 
    MyClass<? extends Map<String, List<? extends Serializable>>> fooField; 
} 

in modo da ottenere un albero di Type s. Ma è un po 'fuori tema. Divertiti :)

0

Questo è un classico esempio del perché la riflessione non è una grande idea.

Ciò che è possibile ottenere da un programma mediante la riflessione sono solo i fatti che le persone del compilatore per la lingua hanno scelto di rendere disponibili.

E generalmente non possono permettersi di rendere tutto disponibile; avrebbero dovuto mantenere il testo del programma grezzo in giro.

Tutte le altre informazioni sul codice non sono quindi disponibili per il reflectee.

La cura per questo è fare un passo al di fuori la lingua e utilizzare uno strumento che può fornire po 'arbitraria di informazioni sul codice. Tali strumenti sono chiamati Program Transformation Systems (PTS).

Un PTS analizza il codice sorgente e crea un AST che lo rappresenta. Un buon PTW costruirà un AST che contiene essenzialmente tutto ciò che riguarda il codice (operatori, operandi, punteggiatura, commenti) in modo che possa essere ispezionato. Normalmente un PTS registra la posizione di riga/colonna dei token di lingua in modo che siano disponibili anche le informazioni di layout; PTS estremo registrerà gli spazi bianchi negli spazi tra i token o almeno saprà leggere il file di testo originale quando necessario, se richiesto. Questo AST è essenzialmente l'equivalente del testo completo che ho detto sarebbe necessario, ma in una forma più comoda da elaborare.

(SPP hanno un altro albergo molto bello: possono modificare l'AST e rigenerare il codice per il programma modificato, ma che è al di sopra e al di là di riflessione quindi non voglio commentare ulteriormente su questo aspetto.).

Problemi correlati