2010-06-07 16 views
8

Questa potrebbe essere una domanda stupida, ma ho appena visto una domanda che chiede how to create a Type variable for a generic type. Il consenso sembrava essere che dovresti avere un metodo fittizio che restituisce quel tipo, e quindi usare reflection per ottenerlo (in questo caso voleva Map<String, String>). Qualcosa di simile a questo:Creazione di oggetto tipo parametrizzato usando la classe anonima

public Map<String, String> dummy() { throw new Error(); } 

Type mapStringString = Class.forName("ThisClass").getMethod("dummy").getGenericReturnType(); 

La mia domanda è, non avendo riflessione utilizzato più di tanto, non poteva semplicemente fare qualcosa di simile:

Type mapStringString = new ParameterizedType() { 
    public Type getRawType() { 
     return Map.class; 
    } 

    public Type getOwnerType() { 
     return null; 
    } 

    public Type[] getActualTypeArguments() { 
     return new Type[] { String.class, String.class }; 
    } 
}; 

Sarebbe questo lavoro? Se no, perché no? E quali sono alcuni dei pericoli/problemi se lo fa (oltre ad essere in grado di restituire un certo tipo, come Integer<String> che non è ovviamente possibile.

+0

Sto provando a fare ciò che la soluzione proposta consente: ottenere un tipo parametrizzato da un raw Tipo ed elenco dei parametri di tipo forniti al momento della compilazione. Hai trovato un altro modo per farlo? – user48956

risposta

6

sicuro che si poteva, e per la maggior parte delle applicazioni che sarebbe probabilmente sufficiente.

Tuttavia, utilizzando il primo metodo, si ottiene un oggetto più raffinato. Ad esempio, si crea un oggetto type1 utilizzando il primo metodo e type2 utilizzando il secondo metodo, quindi type1.equals(type2) restituisce true (poiché il primo metodo restituisce un oggetto che implementa correttamente il metodo equals) ma type2.equals(type1) restituisce false (poiché nel secondo metodo, non hai sovrascritto il metodo equals e stai usando l'implementazione da Object).

stesso ragionamento vale per altri metodi (talvolta utili) come toString, hashCode ecc Il secondo metodo non fornisce implementazioni utili di questi.

+0

In realtà l'implementazione di uguale a type1's (sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl) sembra assegnare il parametro di uguale a ParameterizedType e quindi utilizzare i metodi di interfaccia per confrontare le classi, quindi 'type1.equals (type2)' restituisce true, sebbene il contrario ('type2.equals (type1)') restituisca false; potresti implementare anche quelli, ma poi il codice diventa un po 'grosso –

+0

Sicuro. Puoi ottenere lo stesso risultato nel metodo 2 come nel metodo 1, ma richiede un po 'più di lavoro, come hai notato. – aioobe

2

In realtà, penso che il modo più semplice (== codice minimo) per farlo sarebbe un'interfaccia fittizia che estenda il tipo a cui sei interessato, e quindi getGenericInterfaces()[0] dalla sua classe (usa getGenericSuperclass() se sei interessato a una classe) :

private interface MapStringString extends Map<String, String> {} 
private static ParameterizedType mapStringString(){ 
    return (ParameterizedType) MapStringString.class.getGenericInterfaces()[0]; 
} 

E doen't scala bene, anche se, come è necessario creare una nuova classe per ogni ParameterizedType si vuole rappresentare. Non vedo perché la tua implementazione non funzionerebbe (a meno che non ci siano limiti di cast da qualche parte), e ha il vantaggio allettante che puoi renderlo riutilizzabile.

+0

Sebbene questo sia un 'hackish' come il primo metodo che ho dato, almeno dà un codice migliore in cui si cerca di ottenere il tipo (basta chiamare la funzione invece di ottenere la classe, quindi ottenere il metodo, quindi ottenere un tipo di ritorno generico ciascuno tempo che vuoi usare). –

5

Se includi la libreria di Google Guava nel tuo progetto (dovresti, è fantastico), usa il suo TypeToken per ottenere un tipo. La libreria di Google Gson (per l'interazione con JSON) ha un valore simile a version. Entrambi sono utilizzati come questo (per ottenere un Type che rappresenta List<String>:

Type t = new TypeToken<List<String>>(){}.getType(); 

Se non si vuole fare affidamento su eventuali librerie di terze parti, è comunque possibile utilizzare i tipi anonimi per ottenere un generico concreta tipo con . una riga di codice (questa tecnica non funziona con le interfacce e potrebbe essere più problemi che ne vale la pena per tipi astratti) per ottenere un Type che rappresenta HashMap<String, String>, fare questo:

Type t = new HashMap<String, String>(){}.getClass().getGenericSuperclass(); 

ho verificato che risulta Type istanza .equals() le istanze Type create da Gson 's TypeToken, sebbene non abbiano verificato lo stesso per la versione Guava di TypeToken, a cui non ho accesso al momento.(Guava è una libreria più generica che è così utile per tutti i tipi di cose, dovresti probabilmente usarla comunque.)

+0

Ciò richiede ancora che l'elenco sia noto al momento della compilazione. Nell'OP, il tipo non elaborato e i tipi di parametro non erano noti fino al tipo di compilazione. – user48956

Problemi correlati