2010-09-10 11 views
10

Duplicar posible:
Get generic type of java.util.List¿Cómo obtener el tipo de valor de un mapa en Java?

Tengo un mapa y quiero obtener el tipo de T de una instancia de ese mapa. ¿Cómo puedo hacer eso?

p. Ej. Quiero hacer algo como:

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

Por supuesto que no hay tal función 'getValueType()' en el API.

+4

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

+0

Puede usar [Reflection] (http://download.oracle.com/javase /tutorial/reflect/index.html) – Aillyn

+0

¿Cuál es el requisito funcional? Puede haber mejores soluciones que jugar con la reflexión. – BalusC

Respuesta

8

No puede obtenerlo de la instancia, porque en los genéricos de Java el parámetro de tipo está disponible solo en tiempo de compilación, no en tiempo de ejecución.

Esto se conoce como borrado de tipo . Una definición más formal se proporciona en el JLS.

+2

Esto no es exacto; hay respuestas que dan el código exacto necesario para hacerlo, un par de maneras diferentes –

+2

En realidad esto es 100% correcto para este caso en particular: si todo lo que tiene es dicha instancia, no hay información de tipo para encontrar, nada en absoluto. El mapa en cuestión es de tipo HashMap, ya que los parámetros de tipo aquí se descartan después de la compilación (inducen los cambios de tipo necesarios en el código de bytes). – StaxMan

12

Si su Map es un campo declarada, puede hacer lo siguiente:

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(); 
     } 
    } 
} 

El código anterior imprimir:

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

Sin embargo, si simplemente tiene una instancia de Map, por ejemplo si se pasa a un método, no se puede obtener la información en ese momento:

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

Usted podría agarrar la firma del método (de nuevo, utilizando la reflexión como mi ejemplo anterior) para determinar el tipo de erased, pero realmente no te lleva a ninguna parte (mira la edición a continuación).

Editar:

los comentarios de BalusC a continuación deben tenerse en cuenta. Realmente no deberías necesitar hacer esto; usted ya declaró el tipo de Mapa, por lo que no obtendrá más información de la que ya tiene. Ver su answer here.

+0

Reflection se puede utilizar en tiempo de ejecución en los valores a los que se hace referencia en un mapa. –

+4

Se debe notar que esto solo devuelve los tipos genéricos ** declarados ** (como en la parte 'Mapa map'), no los tipos genéricos ** instanciados ** (como en' new HashMap () 'parte). Como se señaló en mi respuesta en el duplicado vinculado; no tiene sentido imaginarlos de esa manera si ya los ha declarado usted mismo en el código ... – BalusC

+0

@BalusC - De acuerdo; su comentario sobre la cuestión de que existen alternativas de diseño es probablemente la mejor manera de abordar esta cuestión. El código en mi respuesta le da al OP no más información de la que ya sabe en tiempo de compilación. –

1

Aunque se han dado respuestas correctas, hay una alternativa más que aún no se ha señalado. Básicamente, los campos y métodos no son los únicos lugares donde puede vivir la información de tipo genérico: la declaración de superclase también contiene esta información.

Esto significa que si su mapa es en realidad un sub-tipo como:

class MyStringMap extends HashMap<String,String> { } 

entonces es posible averiguar los parámetros de tipo genérico que se utilizaron, llamando 'getGenericSuperclass' (en 'instance.getClass() '), y luego accediendo a los parámetros de tipo reales asignados. Se vuelve bastante complicado ya que uno debe atravesar la jerarquía de tipos para asegurarse de que los parámetros se vinculen correctamente a Map (pueden tener alias, etc.), pero se puede hacer.Y esta es la forma "super token" se utiliza para pasar declaración genérica como:

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

que es utilizado por muchos marcos de Java (Jersey, Guice, Jackson y muchos más)

problema aquí es que, aunque esto permite determinar los tipos nominales de una instancia, solo funciona si hay una subclase no genérica real de tipo genérico, y no conozco una forma de hacer cumplir este requisito (al código de llamada podría resultarle extraño que deba crear una subclase de mapa algo falsa solo para este propósito).

Cuestiones relacionadas