¿Qué está mal con los genéricos de Java? [cerrado]

46

He visto varias veces en este sitio publicaciones que denuncian la implementación de genéricos de Java. Ahora, puedo decir honestamente que no he tenido ningún problema con su uso. Sin embargo, no he intentado hacer una clase genérica yo mismo. Entonces, ¿cuáles son sus problemas con el soporte genérico de Java?

    
pregunta Michael K 30.11.2010 - 21:41

7 respuestas

50

La implementación genérica de Java utiliza borrado de tipo . Esto significa que sus colecciones genéricas fuertemente tipadas son en realidad del tipo Object en el tiempo de ejecución. Esto tiene algunas consideraciones de rendimiento, ya que significa que los tipos primitivos deben estar recuadrados cuando se agregan a una colección genérica. Por supuesto, los beneficios de la corrección de tipo de tiempo de compilación superan la tontería general del borrado de tipo y el enfoque obsesivo en la compatibilidad hacia atrás.

    
respondido por el ChaosPandion 30.11.2010 - 21:54
26

Observe que las respuestas que ya se han proporcionado se concentran en la combinación de Java-the-language, JVM y Java Class Library.

No hay nada malo con los genéricos de Java en lo que respecta a Java, el lenguaje. Como se describe en C # vs Java genéricos , los genéricos de Java son bastante buenos en el nivel del idioma 1 .

Lo que no es óptimo es que la JVM no admite genéricos directamente, lo que tiene varias consecuencias importantes:

  • las partes relacionadas con la reflexión de la Biblioteca de clases no pueden exponer la información de tipo completa que estaba disponible en Java-the-language
  • algunas penalizaciones de rendimiento están involucradas

Supongo que la distinción que estoy haciendo es pedante, ya que Java-the-language se compila de forma ubicua con JVM como el destino y Java Class Library como la biblioteca principal.

1 Con la posible excepción de los comodines, que se cree que hace que la inferencia de tipo sea indecidible en el caso general. Esta es una diferencia importante entre los genéricos de C # y Java que no se menciona muy a menudo. Gracias, antimonio.

    
respondido por el Roman Starkov 01.12.2010 - 04:12
13

La crítica habitual es la falta de reificación. Es decir, los objetos en tiempo de ejecución no contienen información sobre sus argumentos genéricos (aunque la información todavía está presente en campos, métodos, constructores y clases e interfaces extendidas). Esto significa que podría lanzar, por ejemplo, ArrayList<String> a List<File> . El compilador te dará advertencias, pero también te avisará si tomas un ArrayList<String> asignalo a una referencia Object y luego lo conviertes en List<String> . Las ventajas son que con los genéricos probablemente no debería estar haciendo el casting, el rendimiento es mejor sin los datos innecesarios y, por supuesto, la compatibilidad con versiones anteriores.

Algunas personas se quejan de que no se puede sobrecargar sobre la base de argumentos genéricos ( void fn(Set<String>) y void fn(Set<File>) ). Es necesario utilizar mejores nombres de métodos en su lugar. Tenga en cuenta que esta sobrecarga no requerirá una reificación, ya que la sobrecarga es un problema de tiempo de compilación estático.

Los tipos primitivos no funcionan con los genéricos.

Los comodines y los límites son bastante complicados. Son muy útiles. Si Java prefiriera la inmutabilidad y las interfaces de "decir y no preguntar", entonces sería más apropiado utilizar los genéricos del lado de la declaración en lugar de los del lado del uso.

    
respondido por el Tom Hawtin - tackline 30.11.2010 - 23:08
10

Los genéricos de Java apestan porque no puedes hacer lo siguiente:

public class AsyncAdapter<Parser,Adapter> extends AsyncTask<String,Integer,Adapter> {
    proptected Adapter doInBackground(String... keywords) {
      Parser p = new Parser(keywords[0]); // this is an error
      /* some more stuff I was hoping for but couldn't do because
         the compiler wouldn't let me
      */
    }
}

No necesitarías cortar todas las clases en el código anterior para que funcione como debería si los genéricos fueran parámetros genéricos de clase.

    
respondido por el davidk01 10.02.2011 - 05:13
5
  • las advertencias del compilador para los parámetros genéricos faltantes que no sirven para nada hacen que el lenguaje sea verbalmente inútil, por ejemplo, public String getName(Class<?> klazz){ return klazz.getName();}

  • Generics no juegues bien con los arreglos

  • La información de tipo perdida hace que la reflexión se convierta en un caos de fundición y cinta adhesiva.

respondido por el Steve B. 10.02.2011 - 05:59
5

Creo que otras respuestas han dicho esto hasta cierto punto, pero no muy claramente. Uno de los problemas con los genéricos es perder los tipos genéricos durante el proceso de reflexión. Así por ejemplo:

List<String> arr = new ArrayList<String>();
assertTrue( ArrayList.class, arr.getClass() );
TypeVarible[] types = arr.getClass().getTypedVariables();

Desafortunadamente, los tipos devueltos no pueden decirle que los tipos genéricos de arr son String. Es una diferencia sutil, pero es importante. Debido a que arr se crea en tiempo de ejecución, los tipos genéricos se borran en tiempo de ejecución, por lo que no puede darse cuenta de eso. Como algunos indicados ArrayList<Integer> tiene el mismo aspecto que ArrayList<String> desde el punto de vista de la reflexión.

Esto podría no ser importante para el usuario de Genéricos, pero digamos que queremos crear un marco sofisticado que use la reflexión para descubrir cosas extravagantes sobre cómo el usuario declaró los tipos genéricos concretos de una instancia.

Factory<MySpecialObject> factory = new Factory<MySpecialObject>();
MySpecialObject obj = factory.create();

Digamos que queremos que una fábrica genérica cree una instancia de MySpecialObject porque ese es el tipo genérico concreto que declaramos para esta instancia. Bueno, la clase Factory no puede interrogarse a sí misma para averiguar el tipo concreto declarado para esta instancia porque Java los borró.

En los genéricos de .Net puede hacer esto porque en tiempo de ejecución el objeto conoce sus tipos genéricos porque el compilador lo cumplió en el binario. Con borrados Java no puede hacer esto.

    
respondido por el chubbsondubs 01.12.2010 - 03:51
1

Podría decir algunas buenas cosas sobre los genéricos, pero esa no era la pregunta. Podría quejarme de que no están disponibles en tiempo de ejecución y no funcionan con matrices, pero eso se ha mencionado.

Una gran molestia psicológica: ocasionalmente me meto en situaciones donde los genéricos no pueden funcionar. (Las matrices son el ejemplo más simple.) Y no puedo entender si los genéricos no pueden hacer el trabajo o si soy estúpido. Odio eso. Algo así como los genéricos deberían funcionar siempre. Cada vez que no puedo hacer lo que quiero usando Java-the-language, el problema soy yo, y sé que si sigo presionando, llegaré al final. Con los genéricos, si me vuelvo demasiado persistente, puedo perder mucho tiempo.

Pero el problema real es que los genéricos agregan demasiada complejidad por muy poca ganancia. En los casos más simples, puede evitar que agregue una manzana a una lista que contiene autos. Multa. Pero sin los genéricos, este error generaría una ClassCastException realmente rápida en el tiempo de ejecución con poco tiempo perdido. Si agrego un automóvil con un asiento para niños con un niño en él, ¿necesito una advertencia en tiempo de compilación de que la lista es solo para los automóviles con asientos para niños que contienen chimpancés bebés? Una lista de instancias de objetos simples comienza a parecer una buena idea.

El código genérico puede tener muchas palabras y caracteres, ocupa mucho espacio adicional y demora mucho más en leer. Puedo pasar mucho tiempo haciendo que todo ese código extra funcione correctamente. Cuando lo hace, me siento increíblemente inteligente. También he perdido varias horas o más de mi tiempo y me pregunto si alguien más podrá averiguar el código. Me gustaría poder entregarlo para el mantenimiento a personas que son menos inteligentes que yo o que tienen menos tiempo para perder.

Por otra parte, (tengo un poco de nervios y siento la necesidad de proporcionar algo de equilibrio), es agradable cuando se usan colecciones y mapas simples para agregar, poner y verificar cuando está escrito, y generalmente no agrega mucha complejidad al código (if alguien else escribe la colección o el mapa) . Y Java es mejor en esto que C #. Parece que ninguna de las colecciones de C # que quiero usar nunca maneja genéricos. (Admito que tengo gustos extraños en las colecciones).

    
respondido por el RalphChapin 09.02.2012 - 23:04

Lea otras preguntas en las etiquetas

Comentarios Recientes

[EdgedDagger # 7131] ¿Por qué no se permiten métodos especiales? [Dejaré que la gente se centre aquí en formas de mejorar el diseño.] [Abierto] [django-diving # 2520] [La pila de Java en sucesión] [abierto] -Cura-cada elemento sugiere que solo se requiere un tipo de pila para iteradores infinitos, a menos que se conozca como IncompleteFunction, no agregará espacio no infinito a la pila que genere advertencias o funcionalidades desconocidas. Utilice siempre la no superposición en objetos anidados. Java Stackoverflow... Lee mas