2010-09-10 4 views
10

Eventuali duplicati:
Get generic type of java.util.ListCome ottenere il tipo di valore di una mappa in Java?

Ho una mappa e voglio ottenere il tipo di T da un'istanza di quella mappa. Come lo posso fare?

ad es. Voglio fare qualcosa di simile:

Map<String, Double> map = new HashMap<String, Double>(); 
... 
String vtype = map.getValueType().getClass().getName(); //I want to get Double here 

Naturalmente non c'è alcuna funzione come 'getValueType()' nel API.

+4

Vedere questo: http://stackoverflow.com/questions/1942644/get-generic-type-of-java-util-list –

+0

È possibile utilizzare [Reflection] (http://download.oracle.com/javase /tutorial/reflect/index.html) – Aillyn

+0

Qual è il requisito funzionale? Ci possono essere soluzioni migliori di quelle con la riflessione. – BalusC

risposta

8

Non è possibile ottenerlo dall'istanza, perché nei generici Java il parametro type è disponibile solo in fase di compilazione, non in fase di esecuzione.

Questo è noto come tipo di cancellazione. Una definizione più formale è previsto nel JLS.

+2

Questo non è accurato; ci sono risposte che danno il codice esatto necessario per farlo, un paio di modi diversi –

+2

In realtà questo è corretto al 100% per questo caso particolare: se tutto ciò che hai è detto istanza, non ci sono informazioni sul tipo da trovare, assolutamente nulla. La mappa in questione è di tipo HashMap, poiché i parametri di tipo qui vengono scartati dopo la compilazione (inducono i cast di tipo necessari nel codice byte). – StaxMan

12

Se tuo Map è un campo dichiarato, è possibile effettuare le seguenti operazioni:

import java.lang.reflect.*; 
import java.util.*; 

public class Generic { 
    private Map<String, Number> map = new HashMap<String, Number>(); 

    public static void main(String[] args) { 
     try { 
      ParameterizedType pt = (ParameterizedType)Generic.class.getDeclaredField("map").getGenericType(); 
      for(Type type : pt.getActualTypeArguments()) { 
       System.out.println(type.toString()); 
      } 
     } catch(NoSuchFieldException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

Il codice qui sopra stampare:

class java.lang.String 
class java.lang.Number 

Tuttavia, se si dispone semplicemente di un'istanza di Map, ad es. se fosse passato a un metodo, non è possibile ottenere le informazioni in quel momento:

public static void getType(Map<String, Number> erased) { 
    // cannot get the generic type information directly off the "erased" parameter here 
} 

È potrebbe afferrare la firma del metodo (ancora una volta, utilizzando la riflessione come il mio esempio di cui sopra) per determinare il tipo di erased, ma in realtà non ti porta da nessuna parte (vedi modifica sotto).

Edit:

commenti di BalusC sottostanti devono essere notato. Non dovresti davvero aver bisogno di farlo; è già dichiarato il tipo di mappa, quindi non si ottiene più informazioni di quelle già in possesso. Vedi il suo answer here.

+0

Reflection può essere utilizzato in fase di esecuzione sui valori referenziati in una mappa. –

+4

Si noti che questo restituisce solo i tipi generici ** dichiarati ** (come in 'Map map' part), non i ** tipi generati ** istanziati (come in' new HashMap () 'parte). Come indicato nella mia risposta nel duplicato collegato; non ha senso immaginarli in questo modo se li hai già dichiarati nel codice ... – BalusC

+0

@BalusC - Concordato; il tuo commento sulla domanda relativa al fatto che ci siano alternative di progettazione è probabilmente la direzione migliore per rispondere a questa domanda. Il codice nella mia risposta non fornisce all'OP più informazioni di quelle che già conosce in fase di compilazione. –

1

Sebbene siano state fornite risposte corrette, esiste un'altra alternativa che non è stata ancora indicata. Fondamentalmente, campi e metodi non sono gli unici posti in cui possono essere pubblicate informazioni di tipo generico: anche la dichiarazione di super-classe contiene queste informazioni.

Questo significa che se il vostro Map è in realtà un sottotipo del tipo:

class MyStringMap extends HashMap<String,String> { } 

allora è possibile capire parametri di tipo generico che sono stati utilizzati, chiamando 'getGenericSuperclass' (il 'instance.getClass() '), quindi accedere ai parametri di tipo effettivo assegnati. Diventa piuttosto complicato dal momento che si deve attraversare la gerarchia dei tipi per garantire che i parametri vengano correttamente associati a Map (può essere alias, ecc.), Ma può essere fatto.E questo è il modo "super gettone" viene utilizzato per passare dichiarazione generico come:

new TypeToken<Map<String, Double>>() { } 

che viene utilizzato da molti framework Java (Jersey, Guice, Jackson e molti altri)

problema qui è che, anche se ciò consente di determinare i tipi nominali di un'istanza, funziona solo se esiste una sottoclasse non generica effettiva di tipo generico e non conosco un modo per far rispettare questo requisito (il codice chiamante potrebbe trovare strano che sia necessario crea una sottoclasse Map in qualche modo falsa solo per questo scopo).

Problemi correlati