tutorial interfaz gui grafica español ejemplos java generics class literals

interfaz - swing java ejemplos



Java: ¿cómo obtengo un literal de clase de un tipo genérico? (7)

Por lo general, he visto a personas usar literalmente la clase como esta:

Class<Foo> cls = Foo.class;

Pero, ¿y si el tipo es genérico, por ejemplo, List? Esto funciona bien, pero tiene una advertencia ya que la lista debe estar parametrizada:

Class<List> cls = List.class

Entonces, ¿por qué no agregar un <?> ? Bueno, esto causa un error de desajuste de tipo:

Class<List<?>> cls = List.class

Pensé que algo así funcionaría, pero esto es solo un simple error de sintaxis:

Class<List<Foo>> cls = List<Foo>.class

¿Cómo puedo obtener una Class<List<Foo>> estáticamente, por ejemplo, usando el literal de la clase?

Podría usar @SuppressWarnings("unchecked") para eliminar las advertencias causadas por el uso no parametrizado de List en el primer ejemplo, Class<List> cls = List.class , pero prefiero no hacerlo.

¿Alguna sugerencia?


Bueno, como todos sabemos, se borra. Pero se puede conocer en algunas circunstancias donde el tipo se menciona explícitamente en la jerarquía de clases:

import java.lang.reflect.*; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Collectors; public abstract class CaptureType<T> { /** * {@link java.lang.reflect.Type} object of the corresponding generic type. This method is useful to obtain every kind of information (including annotations) of the generic type. * * @return Type object. null if type could not be obtained (This happens in case of generic type whose information cant be obtained using Reflection). Please refer documentation of {@link com.types.CaptureType} */ public Type getTypeParam() { Class<?> bottom = getClass(); Map<TypeVariable<?>, Type> reifyMap = new LinkedHashMap<>(); for (; ; ) { Type genericSuper = bottom.getGenericSuperclass(); if (!(genericSuper instanceof Class)) { ParameterizedType generic = (ParameterizedType) genericSuper; Class<?> actualClaz = (Class<?>) generic.getRawType(); TypeVariable<? extends Class<?>>[] typeParameters = actualClaz.getTypeParameters(); Type[] reified = generic.getActualTypeArguments(); assert (typeParameters.length != 0); for (int i = 0; i < typeParameters.length; i++) { reifyMap.put(typeParameters[i], reified[i]); } } if (bottom.getSuperclass().equals(CaptureType.class)) { bottom = bottom.getSuperclass(); break; } bottom = bottom.getSuperclass(); } TypeVariable<?> var = bottom.getTypeParameters()[0]; while (true) { Type type = reifyMap.get(var); if (type instanceof TypeVariable) { var = (TypeVariable<?>) type; } else { return type; } } } /** * Returns the raw type of the generic type. * <p>For example in case of {@code CaptureType<String>}, it would return {@code Class<String>}</p> * For more comprehensive examples, go through javadocs of {@link com.types.CaptureType} * * @return Class object * @throws java.lang.RuntimeException If the type information cant be obtained. Refer documentation of {@link com.types.CaptureType} * @see com.types.CaptureType */ public Class<T> getRawType() { Type typeParam = getTypeParam(); if (typeParam != null) return getClass(typeParam); else throw new RuntimeException("Could not obtain type information"); } /** * Gets the {@link java.lang.Class} object of the argument type. * <p>If the type is an {@link java.lang.reflect.ParameterizedType}, then it returns its {@link java.lang.reflect.ParameterizedType#getRawType()}</p> * * @param type The type * @param <A> type of class object expected * @return The Class<A> object of the type * @throws java.lang.RuntimeException If the type is a {@link java.lang.reflect.TypeVariable}. In such cases, it is impossible to obtain the Class object */ public static <A> Class<A> getClass(Type type) { if (type instanceof GenericArrayType) { Type componentType = ((GenericArrayType) type).getGenericComponentType(); Class<?> componentClass = getClass(componentType); if (componentClass != null) { return (Class<A>) Array.newInstance(componentClass, 0).getClass(); } else throw new UnsupportedOperationException("Unknown class: " + type.getClass()); } else if (type instanceof Class) { Class claz = (Class) type; return claz; } else if (type instanceof ParameterizedType) { return getClass(((ParameterizedType) type).getRawType()); } else if (type instanceof TypeVariable) { throw new RuntimeException("The type signature is erased. The type class cant be known by using reflection"); } else throw new UnsupportedOperationException("Unknown class: " + type.getClass()); } /** * This method is the preferred method of usage in case of complex generic types. * <p>It returns {@link com.types.TypeADT} object which contains nested information of the type parameters</p> * * @return TypeADT object * @throws java.lang.RuntimeException If the type information cant be obtained. Refer documentation of {@link com.types.CaptureType} */ public TypeADT getParamADT() { return recursiveADT(getTypeParam()); } private TypeADT recursiveADT(Type type) { if (type instanceof Class) { return new TypeADT((Class<?>) type, null); } else if (type instanceof ParameterizedType) { ArrayList<TypeADT> generic = new ArrayList<>(); ParameterizedType type1 = (ParameterizedType) type; return new TypeADT((Class<?>) type1.getRawType(), Arrays.stream(type1.getActualTypeArguments()).map(x -> recursiveADT(x)).collect(Collectors.toList())); } else throw new UnsupportedOperationException(); } } public class TypeADT { private final Class<?> reify; private final List<TypeADT> parametrized; TypeADT(Class<?> reify, List<TypeADT> parametrized) { this.reify = reify; this.parametrized = parametrized; } public Class<?> getRawType() { return reify; } public List<TypeADT> getParameters() { return parametrized; } }

Y ahora puedes hacer cosas como:

static void test1() { CaptureType<String> t1 = new CaptureType<String>() { }; equals(t1.getRawType(), String.class); } static void test2() { CaptureType<List<String>> t1 = new CaptureType<List<String>>() { }; equals(t1.getRawType(), List.class); equals(t1.getParamADT().getParameters().get(0).getRawType(), String.class); } private static void test3() { CaptureType<List<List<String>>> t1 = new CaptureType<List<List<String>>>() { }; equals(t1.getParamADT().getRawType(), List.class); equals(t1.getParamADT().getParameters().get(0).getRawType(), List.class); } static class Test4 extends CaptureType<List<String>> { } static void test4() { Test4 test4 = new Test4(); equals(test4.getParamADT().getRawType(), List.class); } static class PreTest5<S> extends CaptureType<Integer> { } static class Test5 extends PreTest5<Integer> { } static void test5() { Test5 test5 = new Test5(); equals(test5.getTypeParam(), Integer.class); } static class PreTest6<S> extends CaptureType<S> { } static class Test6 extends PreTest6<Integer> { } static void test6() { Test6 test6 = new Test6(); equals(test6.getTypeParam(), Integer.class); } class X<T> extends CaptureType<T> { } class Y<A, B> extends X<B> { } class Z<Q> extends Y<Q, Map<Integer, List<List<List<Integer>>>>> { } void test7(){ Z<String> z = new Z<>(); TypeADT param = z.getParamADT(); equals(param.getRawType(), Map.class); List<TypeADT> parameters = param.getParameters(); equals(parameters.get(0).getRawType(), Integer.class); equals(parameters.get(1).getRawType(), List.class); equals(parameters.get(1).getParameters().get(0).getRawType(), List.class); equals(parameters.get(1).getParameters().get(0).getParameters().get(0).getRawType(), List.class); equals(parameters.get(1).getParameters().get(0).getParameters().get(0).getParameters().get(0).getRawType(), Integer.class); } static void test8() throws IllegalAccessException, InstantiationException { CaptureType<int[]> type = new CaptureType<int[]>() { }; equals(type.getRawType(), int[].class); } static void test9(){ CaptureType<String[]> type = new CaptureType<String[]>() { }; equals(type.getRawType(), String[].class); } static class SomeClass<T> extends CaptureType<T>{} static void test10(){ SomeClass<String> claz = new SomeClass<>(); try{ claz.getRawType(); throw new RuntimeException("Shouldnt come here"); }catch (RuntimeException ex){ } } static void equals(Object a, Object b) { if (!a.equals(b)) { throw new RuntimeException("Test failed. " + a + " != " + b); } }

Más información here . Pero, nuevamente, es casi imposible de recuperar por:

class SomeClass<T> extends CaptureType<T>{} SomeClass<String> claz = new SomeClass<>();

donde se borra


Debido al hecho de que los literales de clase no tienen información de tipo genérico, creo que debe suponer que será imposible deshacerse de todas las advertencias. En cierto modo, usar Class<Something> es lo mismo que usar una colección sin especificar el tipo genérico. Lo mejor que pude sacar fue:

private <C extends A<C>> List<C> getList(Class<C> cls) { List<C> res = new ArrayList<C>(); // "snip"... some stuff happening in here, using cls return res; } public <C extends A<C>> List<A<C>> getList() { return getList(A.class); }


No hay literales de clase para tipos parametrizados, sin embargo, hay objetos de tipo que definen correctamente estos tipos.

Consulte java.lang.reflect.ParameterizedType - http://java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/ParameterizedType.html

La biblioteca Gson de Google define una clase TypeToken que permite simplemente generar tipos parametrizados y lo utiliza para especificar objetos json con tipos de parámetros complejos de una manera amigable genérica. En tu ejemplo usarías:

Type typeOfListOfFoo = new TypeToken<List<Foo>>(){}.getType()

Tenía la intención de publicar enlaces a las clases TypeToken y Gson javadoc, pero no me permite publicar más de un enlace, ya que soy un nuevo usuario, puedes encontrarlos fácilmente con la búsqueda de Google.


No puede debido al borrado de tipo .

Los genéricos de Java son poco más que azúcar sintáctico para moldes de objetos. Demostrar:

List<Integer> list1 = new ArrayList<Integer>(); List<String> list2 = (List<String>)list1; list2.add("foo"); // perfectly legal

El único caso en el que la información de tipo genérico se conserva en el tiempo de ejecución es con Field.getGenericType() si se interroga a los miembros de una clase a través de la reflexión.

Todo esto es por lo que Object.getClass() tiene esta firma:

public final native Class<?> getClass();

La parte importante es la Class<?> .

Para decirlo de otra manera, de las preguntas frecuentes de Java Generics :

¿Por qué no hay literal de clase para tipos parametrizados concretos?

Porque el tipo parametrizado no tiene una representación exacta del tipo de tiempo de ejecución.

Un literal de clase denota un objeto Class que representa un tipo dado. Por ejemplo, la clase literal String.class denota el objeto Class que representa el tipo String y es idéntico al objeto Class que se devuelve cuando se invoca el método getClass en un objeto String . Un literal de clase se puede usar para verificaciones del tipo de tiempo de ejecución y para la reflexión.

Los tipos parametrizados pierden sus argumentos de tipo cuando se convierten a código de bytes durante la compilación en un proceso llamado borrado de tipo. Como efecto secundario del borrado de tipo, todas las instancias de un tipo genérico comparten la misma representación de tiempo de ejecución, concretamente la del tipo sin formato correspondiente. En otras palabras, los tipos parametrizados no tienen una representación de tipo propia. En consecuencia, no tiene sentido formar literales de clase tales como List<String>.class , List<Long>.class y List<?>.class , ya que no existen dichos objetos Class . Solo la List tipo sin List tiene un objeto Class que representa su tipo de tiempo de ejecución. Se conoce como List.class .


Para exponer la respuesta de cletus, en el tiempo de ejecución se elimina todo el registro de los tipos genéricos. Los genéricos se procesan solo en el compilador y se utilizan para proporcionar seguridad de tipo adicional. En realidad, son taquigrafía que le permite al compilador insertar tipos de letra en los lugares apropiados. Por ejemplo, antes tendrías que hacer lo siguiente:

List x = new ArrayList(); x.add(new SomeClass()); Iterator i = x.iterator(); SomeClass z = (SomeClass) i.next();

se convierte

List<SomeClass> x = new ArrayList<SomeClass>(); x.add(new SomeClass()); Iterator<SomeClass> i = x.iterator(); SomeClass z = i.next();

Esto le permite al compilador verificar su código en tiempo de compilación, pero en tiempo de ejecución aún se ve como el primer ejemplo.


Puede usar un método auxiliar para deshacerse de @SuppressWarnings("unchecked") toda una clase.

@SuppressWarnings("unchecked") private static <T> Class<T> generify(Class<?> cls) { return (Class<T>)cls; }

Entonces podrías escribir

Class<List<Foo>> cls = generify(List.class);

Otros ejemplos de uso son

Class<Map<String, Integer>> cls; cls = generify(Map.class); cls = TheClass.<Map<String, Integer>>generify(Map.class); funWithTypeParam(generify(Map.class)); public void funWithTypeParam(Class<Map<String, Integer>> cls) { }

Sin embargo, dado que rara vez es realmente útil, y el uso del método derrota la verificación de tipos del compilador, no recomendaría implementarlo en un lugar donde sea de acceso público.


Puedes gestionarlo con un doble elenco:

@SuppressWarnings("unchecked") Class<List<Foo>> cls = (Class<List<Foo>>)(Object)List.class