Esto surgió como una pregunta que hice en una entrevista recientemente como algo que el candidato deseaba que se agregara al lenguaje Java. Es comúnmente identificado como una molestia que Java no tenga genéricos reificados pero, cuando se le presionó, el candidato no pudo decirme el tipo de cosas que podría haber logrado si estuvieran allí.
Obviamente, debido a que los tipos sin procesar están permitidos en Java (y las comprobaciones inseguras), es posible subvertir los genéricos y terminar con un List<Integer>
que (por ejemplo) realmente contiene String
s. Esto claramente podría volverse imposible si la información tipográfica se reificara; ¡Pero debe haber más que esto !
¿Podrían las personas publicar ejemplos de cosas que realmente querrían hacer si estuvieran disponibles los genéricos reificados? Quiero decir, obviamente podrías obtener el tipo de List
en tiempo de ejecución, pero ¿qué harías con él?
public <T> void foo(List<T> l) {
if (l.getGenericType() == Integer.class) {
//yeah baby! err, what now?
EDITAR : Una actualización rápida de esto, ya que las respuestas parecen estar principalmente preocupadas por la necesidad de pasar a Class
como parámetro (por ejemplo EnumSet.noneOf(TimeUnit.class)
). Estaba buscando más algo parecido a donde esto simplemente no es posible . Por ejemplo:
List<?> l1 = api.gimmeAList();
List<?> l2 = api.gimmeAnotherList();
if (l1.getGenericType().isAssignableFrom(l2.getGenericType())) {
l1.addAll(l2); //why on earth would I be doing this anyway?
fuente
findClass()
tendría que ignorar la parametrización, perodefineClass()
no podría). Y como sabemos, The Powers That Be mantienen la compatibilidad con versiones anteriores de manera primordial.Respuestas:
De las pocas veces que me encontré con esta "necesidad", finalmente se reduce a esta construcción:
Esto funciona en C # asumiendo que
T
tiene un constructor predeterminado . Incluso puede obtener el tipo de tiempo de ejecucióntypeof(T)
y obtener los constructoresType.GetConstructor()
.La solución común de Java sería pasar el
Class<T>
argumento as.(no es necesario pasarlo como argumento de constructor, ya que un argumento de método también está bien, lo anterior es solo un ejemplo, también
try-catch
se omite por brevedad)Para todas las demás construcciones de tipo genérico, el tipo real se puede resolver fácilmente con un poco de ayuda de reflexión. Las siguientes preguntas y respuestas ilustran los casos de uso y las posibilidades:
fuente
T.class
oT.getClass()
, por lo que podría acceder a todos sus campos, constructores y métodos. Hace que la construcción también sea imposible.Lo que más me muerde es la incapacidad de aprovechar el envío múltiple en múltiples tipos genéricos. Lo siguiente no es posible y hay muchos casos en los que sería la mejor solución:
fuente
public void my_method(List input) {}
. Sin embargo, nunca me he encontrado con esta necesidad, simplemente porque no tendrían el mismo nombre. Si tienen el mismo nombre, me preguntaría sipublic <T extends Object> void my_method(List<T> input) {}
no es una mejor idea.myStringsMethod(List<String> input)
emyIntegersMethod(List<Integer> input)
incluso si la sobrecarga para tal caso fuera posible en Java.<algorithm>
en C ++.Me viene a la mente la seguridad de tipos . Downcasting a un tipo parametrizado siempre será inseguro sin genéricos reificados:
Además, las abstracciones se filtrarían menos , al menos las que pueden estar interesadas en información de tiempo de ejecución sobre sus parámetros de tipo. Hoy en día, si necesita algún tipo de información de tiempo de ejecución sobre el tipo de uno de los parámetros genéricos, también debe transmitirlo
Class
. De esa manera, su interfaz externa depende de su implementación (ya sea que use RTTI sobre sus parámetros o no).fuente
ParametrizedList
que copia los datos en los tipos de verificación de la colección de origen. Es un poco parecido,Collections.checkedList
pero se puede sembrar con una colección para empezar.T.class.getAnnotation(MyAnnotation.class)
(dondeT
está un tipo genérico) sin cambiar la interfaz externa.Podrías crear matrices genéricas en tu código.
fuente
String[] strings = new String[1]; Object[] objects = strings; objects[0] = new Object();
Se compila bien en ambos idiomas. Funciona notsofine.Esta es una pregunta antigua, hay un montón de respuestas, pero creo que las respuestas existentes están fuera de lugar.
"reificado" solo significa real y generalmente solo significa lo opuesto al borrado de tipo.
El gran problema relacionado con Java Generics:
void method(List<A> l)
ymethod(List<B> l)
. Esto se debe al borrado de tipos, pero es extremadamente mezquino.fuente
deserialize(thingy, List<Integer>.class)
La serialización sería más sencilla con la cosificación. Lo que querríamos es
Lo que tenemos que hacer es
se ve feo y funciona de manera extraña.
También hay casos en los que sería muy útil decir algo como
Estas cosas no pican a menudo, pero sí pican cuando ocurren.
fuente
Mi exposición a Java Geneircs es bastante limitada, y aparte de los puntos que otras respuestas ya han mencionado, hay un escenario explicado en el libro Java Generics and Collections , de Maurice Naftalin y Philip Walder, donde los genéricos reificados son útiles.
Dado que los tipos no son confiables, no es posible tener excepciones parametrizadas.
Por ejemplo, la declaración del formulario siguiente no es válida.
Esto se debe a que la cláusula catch comprueba si la excepción lanzada coincide con un tipo determinado. Esta verificación es la misma que la verificación realizada por la prueba de instancia y dado que el tipo no es confiable, la forma de declaración anterior no es válida.
Si el código anterior fuera válido, entonces habría sido posible el manejo de excepciones de la siguiente manera:
El libro también menciona que si los genéricos de Java se definen de manera similar a la forma en que se definen las plantillas de C ++ (expansión), puede conducir a una implementación más eficiente ya que esto ofrece más oportunidades de optimización. Pero no ofrece ninguna explicación más que esta, por lo que cualquier explicación (consejos) de la gente conocedora sería útil.
fuente
Las matrices probablemente funcionarían mucho mejor con los genéricos si estuvieran cosificados.
fuente
List<String>
no es unList<Object>
.Tengo un contenedor que presenta un conjunto de resultados jdbc como un iterador (significa que puedo realizar pruebas unitarias de operaciones originadas en la base de datos mucho más fácilmente mediante la inyección de dependencias).
La API se parece a
Iterator<T>
donde T es un tipo que se puede construir usando solo cadenas en el constructor. El iterador luego mira las cadenas que se devuelven de la consulta sql y luego intenta hacer coincidirlas con un constructor de tipo T.En la forma actual en que se implementan los genéricos, también tengo que pasar la clase de los objetos que crearé a partir de mi conjunto de resultados. Si entiendo correctamente, si los genéricos fueran reificados, podría simplemente llamar a T.getClass () para obtener sus constructores, y luego no tener que emitir el resultado de Class.newInstance (), que sería mucho más ordenado.
Básicamente, creo que facilita la escritura de API (en lugar de simplemente escribir una aplicación), porque puede inferir mucho más de los objetos y, por lo tanto, será necesaria menos configuración ... No aprecié las implicaciones de las anotaciones hasta que los vi siendo usados en cosas como spring o xstream en lugar de montones de config.
fuente
Una cosa buena sería evitar el boxing para tipos primitivos (de valor). Esto está relacionado de alguna manera con la queja de la matriz que otros han planteado, y en los casos en que el uso de la memoria está restringido, en realidad podría marcar una diferencia significativa.
También hay varios tipos de problemas al escribir un marco donde es importante poder reflexionar sobre el tipo parametrizado. Por supuesto, esto se puede solucionar pasando un objeto de clase en tiempo de ejecución, pero esto oscurece la API y coloca una carga adicional sobre el usuario del marco.
fuente
new T[]
donde T es de tipo primitivo!No es que consigas algo extraordinario. Será más sencillo de entender. El borrado de tipos parece un momento difícil para los principiantes y, en última instancia, requiere la comprensión de la forma en que funciona el compilador.
Mi opinión es que los genéricos son simplemente un extra que ahorra mucho casting redundante.
fuente
Algo que todas las respuestas aquí han pasado por alto y que es constantemente un dolor de cabeza para mí es que, dado que los tipos se borran, no se puede heredar una interfaz genérica dos veces. Esto puede ser un problema cuando desea crear interfaces de grano fino.
fuente
Aquí hay uno que me atrapó hoy: sin cosificación, si escribe un método que acepta una lista varargs de elementos genéricos ... las personas que llaman pueden PENSAR que son seguras, pero accidentalmente pasan cualquier basura vieja y explotan su método.
¿Parece poco probable que eso suceda? ... Claro, hasta que ... use Class como su tipo de datos. En este punto, la persona que llama le enviará con mucho gusto muchos objetos de clase, pero un simple error tipográfico le enviará objetos de clase que no se adhieren a T y se producirá un desastre.
(NB: Puede que haya cometido un error aquí, pero al buscar en Google "varargs genéricos", lo anterior parece ser justo lo que cabría esperar. Lo que hace que esto sea un problema práctico es el uso de Class, creo: las personas que llaman parecen tener menos cuidado :()
Por ejemplo, estoy usando un paradigma que usa objetos Class como clave en mapas (es más complejo que un mapa simple, pero conceptualmente eso es lo que está sucediendo).
por ejemplo, esto funciona muy bien en Java Generics (ejemplo trivial):
por ejemplo, sin reificación en Java Generics, este acepta CUALQUIER objeto "Class". Y es solo una pequeña extensión del código anterior:
Los métodos anteriores deben escribirse miles de veces en un proyecto individual, por lo que la posibilidad de error humano aumenta. Depurar errores está demostrando que "no es divertido". Actualmente estoy tratando de encontrar una alternativa, pero no tengo muchas esperanzas.
fuente