Pregunta Cómo crear un literal de clase de un tipo conocido: Clase >


Tome lo siguiente:

public Class<List<String>> getObjectType() {
    // what can I return here?
}

¿Qué expresión literal de clase puedo devolver de este método que satisfaga los genéricos y compile? List.class no compilará, y tampoco lo hará List.<String>class.

Si se está preguntando "por qué", estoy escribiendo una implementación de Spring FactoryBean<List<String>>, lo cual requiere que implemente Class<List<String>> getObjectType(). Sin embargo, esto es no una pregunta de primavera.

editar: Mis gritos lastimeros han sido escuchados por los poderes que están en SpringSource, por lo que Spring 3.0.1 tendrá el tipo de devolución de getObjectType() cambiado a Class<?>, que claramente evita el problema.


50
2018-01-06 10:45


origen


Respuestas:


Siempre puedes elegir lo que necesites, como este

return (Class<List<String>>) new ArrayList<String>().getClass();

o

return (Class<List<String>>) Collections.<String>emptyList().getClass();

Pero supongo que eso no es lo que buscas. Bueno, funciona, con una advertencia, pero no es exactamente "hermoso".

Acabo de encontrar esto

¿Por qué no hay literal de clase para los tipos con parámetros comodín?

Porque un tipo parametrizado de comodín no tiene una representación exacta del tipo de tiempo de ejecución.

Así que el casting podría ser el único camino a seguir.


37
2018-01-06 11:13



Nunca deberías usar la construcción Class<List<String>>. No tiene sentido, y debería producir una advertencia en Java (pero no lo hace). Las instancias de clase siempre representan tipos crudos, por lo que puede tener Class<List>; Eso es. Si desea que algo represente un tipo genérico reificado como List<String>, necesitas un "token de tipo súper" como el que usa Guice:

http://google-guice.googlecode.com/git/javadoc/com/google/inject/TypeLiteral.html


15
2018-01-06 18:30



La existencia de un Class<List<String>> es intrínsecamente peligroso. este es el por qué:

// This statement generates a warning - for a reason...
Class<List<String>> unsafeListClass = (Class<List<String>>) (Class<?>) List.class;

List<Integer> integerList = new ArrayList<Integer>(); // Ok
integerList.add(42); // Ok

System.out.println(unsafeListClass.isInstance(integerList)); // Prints "true".
List<String> stringList =
   unsafeListClass.cast(integerList); // Succeeds, with no warning!
stringList.add("Hello, World!"); // Also succeeds with no warning

for (int x: integerList) {
    // Compiles without warning, but throws ClassCastException at runtime
    System.out.println(100-x);
}

7
2018-02-12 00:12



Encontró este enlace en springframework.org que da alguna idea.

P.ej.

List<String> myList = new ArrayList<String>();
return (Class<List<String>>)myList.getClass();

6
2018-01-06 11:06



Puede implementar ese método de esta manera:

public Class<List<String>> getObjectType() {
    return (Class<List<String>>) ((Class)List.class);
}

4
2018-04-13 16:15



Mira esta discusión en los foros de SUN:

http://forums.sun.com/thread.jspa?threadID=5253007

Y la publicación de blog a la que se hace referencia que describe un trabajo alternativo utilizando "tokens de tipo super":

http://gafter.blogspot.com/2006/12/super-type-tokens.html


2
2018-01-06 11:21



No estoy seguro de si esto es posible en absoluto, ya que cualquier literal de clase se compilará para Class.forName(...) y dado que esto sucede en tiempo de ejecución, no existe información genérica.


1
2018-01-06 11:08



No estoy seguro, pero la siguiente pregunta que le hice podría ser relevante para usted ...

parámetros y operaciones del tipo java genericics en esos tipos


0
2018-01-06 11:01



¿Qué tal esto?

public class TestMain {
    public static void main(String[] args) throws Exception {
        Type type = TestMain.class.getMethod("dummy").getGenericReturnType();
        System.out.println("type = " + type);
    }

    public List<Integer> dummy() {return null;}
}

Esto imprime:

type = java.util.List<java.lang.Integer>

0
2018-05-09 09:11



El siguiente enfoque es problemático:

> public Class<List<String>> getModelType() {
>   return (Class<List<String>>) new ArrayList<String>().getClass();
> }

p.ej. si quieres probar si un objeto dice de tipo

org.eclipse.emf.common.util.BasicEList<String> 

es de tipo

List<String> 

basado en el resultado del método getModelType () antes mencionado, por ejemplo:

BasicEList<String> fromObject = ...;
if (getModelType().isAssignableFrom(fromObject.getClass())) {
    transferFromModelToUi(getModelType().cast(fromObject));
}

resultará en falso, mientras que debería ser cierto porque ambos objetos implementan la lista de interfaz (ya que getModelType () devuelve un objeto de clase de tipo List y no ArrayList).

Aquí hay un enfoque que funcionó para mí (un poco engorroso pero que lleva a resultados correctos en el ejemplo anterior, podría moverse a un inicializador estático):

public Class<List<String>> getModelType() {
    Class<?> arrayListClass = new ArrayList<String>().getClass();
    Class<?>[] interfaces = arrayListClass.getInterfaces();
    int index = 0;
    for (int i = 0; i < interfaces.length; i++) {
        if (interfaces[i].equals(List.class)) {
            index = i;
            break;
        }
    }
    return (Class<List<String>>) interfaces[index];
}

0
2018-03-28 07:54