2011-12-08 15 views
9

acabo de aprender acerca de este fine looking syntax¿Cómo obtener la clase de tipo genérico cuando no hay ningún parámetro de la misma?

Collections.<String>emptyList() 

para obtener una List vacío con elementos que son supuestamente de tipo String. fuente de Java es el siguiente:

public static final List EMPTY_LIST = new EmptyList<Object>(); 
: 
public static final <T> List<T> emptyList() { 
    return (List<T>) EMPTY_LIST; 
} 

Ahora bien, si el código A método de ese modo en el que el tipo genérico no aparece en la lista de parámetros, ¿hay alguna manera de cómo puedo acceder a la clase real que se convierte en T?

que digo, hasta ahora mi enfoque para codificar el mismo que habría sido

private <T> T get(String key, Class<T> clazz) { 
    // here I can do whatever I want with clazz, e.g.: 
    return clazz.cast(value); 
} 

Si me quita el clazz -parámetro yo no sería capaz de hacer lo cast(). Obviamente, podría hacer

return (T) value; 

pero eso me da la advertencia habitual Type safety: Unchecked cast from Object to T. Ok, @SuppressWarnings("unchecked") ayuda aquí, pero en realidad quiero hacer algo con el tipo de devolución previsto del método. Si añado una variable local

T retValue; 

Tendría que inicializar con algo, null no ayuda. Después de asignarlo como

@SuppressWarnings("unchecked") 
T retValue = (T) value; 

Podría hacerlo, p.

retValue.getClass().getName() 

pero si el elenco no termino con ninguna información sobre T nuevo.

Dado que Java (o al menos mi Java 6) ya no tiene la información genérica durante el tiempo de ejecución, actualmente no se me ocurre una manera de hacerlo. ¿Hay alguna manera? ¿O tengo que seguir con mi "viejo" enfoque aquí?

Tenga en cuenta que el ejemplo que alineé es muy simple y no tiene mucho sentido. Quiero hacer cosas más complicadas aquí, pero eso está fuera del alcance.

+0

parece un duplicado de [esta pregunta] (http://stackoverflow.com/questions/2434041/instantiating-generics-type-in-java) –

+0

@NitzanVolman: Hasta cierto punto, usted tiene razón, pero no es completamente. – sjngm

Respuesta

5

Si desea el tipo genérico en tiempo de ejecución, debe tenerlo como campo o crear una subclase de un tipo para una combinación específica de tipos.

p. Ej.

List<String> list = new ArrayList<String>() {}; // creates a generic sub-type 
final Class type = (Class) ((ParameterizedType) list.getClass() 
          .getGenericSuperclass()).getActualTypeArguments()[0]; 
System.out.println(type); 

impresiones

class java.lang.String 
+0

Eso es genial, no sabía de eso. –

+1

es decir, no puede obtener el tipo genérico de una clase, pero puede obtener los tipos de una súper clase (y súper interfaces) –

+0

@PeterLawrey: Whow, gracias. Sin embargo, si uso 'List list = new ArrayList () {};' no funciona. 'getActualTypeArguments() [0]' es un 'sun.reflect.generics.reflectiveObjects.TypeVariableImpl'. ¿Tiene sentido? – sjngm

1

Como mencionaste, los genéricos de Java son solo tiempo de compilación. No se usan en tiempo de ejecución.

Debido a esto, el enfoque anterior que estaba utilizando será su única forma de lograrlo.

4

No puede, desafortunadamente. Todos los genéricos y los parámetros de tipo se borran en tiempo de ejecución. Así, en el tiempo de ejecución del tipo de su T es simplemente Object

+2

De acuerdo, tengo varios métodos como 'privado T get (clave de cadena, clase clazz)' debido a esto. – Dave

2

retValue.getClass().getName() siempre devolverá el tipo de ejecución del objeto y no el nombre de la clase del tipo de parámetro.

Si desea obtener la clase de parámetro, no hay otra manera que utilizar su primera solución. (Es decir, pase explícitamente el objeto de clase.)

+0

Tienes razón, ese hubiera sido otro efecto secundario que no quería que sucediera. – sjngm

-2

me entero de que hay una solución para conseguir Class<?> de T:

public class Action<T>{ 
} 

public Class<?> getGenericType(Object action) throws ClassNotFoundException{ 
    Type type = 
    ((ParameterizedType)action.getClass().getGenericSuperclass()) 
     .getActualTypeArguments()[0]; 
    String sType[] = type.toString().split(" "); 

    if(sType.length != 2) 
     throw new ClassNotFoundException(); 

    return Class.forName(sType[1]); 
} 

El uso del código anterior:

Action<String> myAction = new Action<String>(); 

getGenericType(myAction); 

I no probé esto con prim tipos de entrada (int, byte, boolean).

Creo que no es muy rápido, pero no tiene que pasar Class<?> al constructor.

EDIT:

El uso anterior no es correcto, porque superclase genérico no está disponible para Action<String>. La superclase genérica estará disponible solo para clases heredadas como class ActionWithParam extends Action<String>{}. Esta es la razón por la que cambié de opinión y ahora sugiero pasar el parámetro de clase al constructor también. Gracias a newacct para la corrección.

+0

esto está mal .. – newacct

+0

¿Qué pasa? Sé que debería haber una verificación e iteración de longitud en ((ParameterizedType) action.getClass(). GetGenericSuperclass()). GetActualTypeArgum ents() y la comparación de sType [0] con "clase". Lo uso en mi invocación de método remoto personalizado y funciona. Por favor, dame algunas pistas si hay algún otro problema. – ivanesko

+0

el código como se muestra no "funciona" – newacct

Cuestiones relacionadas