tutorial libro interfaz gui grafica español ejemplos componentes clase java generics

libro - ¿Por qué algunos afirman que la implementación de genéricos de Java es mala?



java swing tutorial pdf español (13)

  1. Implementación en tiempo de ejecución (es decir, no borrado de tipo);
  2. La capacidad de usar tipos primitivos (esto está relacionado con (1));
  3. Si bien el wildcarding es útil, la sintaxis y saber cuándo usarlo es algo que afecta a mucha gente. y
  4. No mejora el rendimiento (debido a (1); los genéricos de Java son azúcar sintáctica para objetos de fundición).

(1) conduce a un comportamiento muy extraño. El mejor ejemplo en el que puedo pensar es. Asumir:

public class MyClass<T> { T getStuff() { ... } List<String> getOtherStuff() { ... } }

luego declara dos variables:

MyClass<T> m1 = ... MyClass m2 = ...

Ahora llame a getOtherStuff() :

List<String> list1 = m1.getOtherStuff(); List<String> list2 = m2.getOtherStuff();

El segundo tiene su argumento de tipo genérico eliminado por el compilador porque es un tipo sin formato (lo que significa que el tipo parametrizado no se suministra) aunque no tenga nada que ver con el tipo parametrizado.

También mencionaré mi declaración favorita del JDK:

public class Enum<T extends Enum<T>>

Además de wildcarding (que es una mezcla), creo que los genéricos de .Net son mejores.

Ocasionalmente escuché que con los genéricos, Java no lo hacía bien. (referencia más cercana, here )

Disculpe mi inexperiencia, pero ¿qué los hubiera hecho mejores?


El mayor problema es que los genéricos de Java son solo de tiempo de compilación, y puede subvertirlos en tiempo de ejecución. C # es elogiado porque hace más comprobaciones en tiempo de ejecución. Hay una discusión realmente buena en esta publicación y se vincula con otras discusiones.


El principal problema es que Java no tiene genéricos en tiempo de ejecución. Es una característica de tiempo de compilación.

Cuando creas una clase genérica en Java, usan un método llamado "Borrado de tipo" para eliminar realmente todos los tipos genéricos de la clase y esencialmente reemplazarlos con Object. La versión de milla de genéricos es que el compilador simplemente inserta moldes en el tipo genérico especificado siempre que aparezca en el cuerpo del método.

Esto tiene muchas desventajas. Uno de los más grandes, en mi humilde opinión, es que no puede usar la reflexión para inspeccionar un tipo genérico. Los tipos no son realmente genéricos en el código de bytes y, por lo tanto, no se pueden inspeccionar como genéricos.

Gran resumen de las diferencias aquí: http://www.jprl.com/Blog/archive/development/2007/Aug-31.html


Ignorando todo el tipo de borrado, los genéricos como se especifican simplemente no funcionan.

Esto compila:

List<Integer> x = Collections.emptyList();

Pero este es un error de sintaxis:

foo(Collections.emptyList());

Donde foo se define como:

void foo(List<Integer> x) { /* method body not important */ }

Entonces, si un tipo de expresión comprueba depende de si se está asignando a una variable local o un parámetro real de una llamada a un método. ¿Qué tan loco es eso?


Java no aplica genéricos en tiempo de ejecución, solo en tiempo de compilación.

Esto significa que puede hacer cosas interesantes, como agregar los tipos incorrectos a Colecciones genéricas.


La introducción de genéricos en Java fue una tarea difícil porque los arquitectos estaban tratando de equilibrar la funcionalidad, la facilidad de uso y la compatibilidad con el código heredado. Muy esperado, los compromisos se tuvieron que hacer.

Hay algunos que también sienten que la implementación de genéricos de Java aumentó la complejidad del lenguaje a un nivel inaceptable (ver " Genéricos considerados dañinos " de Ken Arnold). Las preguntas frecuentes sobre genéricos de Angelika Langer dan una muy buena idea de lo complicadas que pueden llegar a ser las cosas.


Los genéricos de Java son solo de tiempo de compilación y se compilan en código no genérico. En C #, el MSIL compilado real es genérico. Esto tiene enormes implicaciones para el rendimiento debido a que Java aún se lanza durante el tiempo de ejecución. Mira aquí para más .


Malo:

  • La información de tipo se pierde en el momento de la compilación, por lo que en el momento de la ejecución no se puede decir qué tipo "se supone" que debe ser
  • No se puede usar para tipos de valor (esto es un problema: en .NET, una List<byte> realmente está respaldada por un byte[] por ejemplo, y no se requiere boxeo)
  • Sintaxis para invocar métodos genéricos sucks (IMO)
  • La sintaxis de las restricciones puede ser confusa
  • Wildcarding es generalmente confuso
  • Varias restricciones debido al lanzamiento anterior, etc.

Bueno:

  • Wildcarding permite que se especifique la covarianza / contravarianza en el lado de la llamada, lo cual es muy claro en muchas situaciones
  • ¡Es mejor que nada!

Ojalá fuera una wiki para poder agregar a otras personas ... pero ...

Problemas:

  • Tipo Borrado (sin disponibilidad en tiempo de ejecución)
  • Sin soporte para tipos primative
  • Incompatibilidad con las anotaciones (ambas se agregaron en 1.5. Todavía no estoy seguro de por qué las anotaciones no permiten los medicamentos genéricos, aparte de apresurar las funciones)
  • Incompatibilidad con Arrays. (A veces, realmente quiero hacer algo como Class < ? Extends MyObject > [], pero no estoy permitido)
  • Wierd comodín sintaxis y comportamiento
  • El hecho de que el soporte genérico es inconsistente en las clases de Java. Lo agregaron a la mayoría de los métodos de recolección, pero de vez en cuando, te encuentras con una instancia donde no está allí.

Otro efecto secundario de que sean tiempo de compilación y no tiempo de ejecución es que no puede llamar al constructor del tipo genérico. Entonces no puedes usarlos para implementar una fábrica genérica ...

public class MyClass { public T getStuff() { return new T(); } }

--jeffk ++


Se comprueba la corrección de los genéricos de Java en el momento de la compilación y luego se elimina toda la información de tipo (el proceso se denomina borrado de tipo . Por lo tanto, la List<Integer> genérica List<Integer> se reducirá a su lista cruda , no genérica, que puede contener objetos de clase.

Esto permite insertar objetos arbitrarios en la lista en tiempo de ejecución, y ahora es imposible saber qué tipos se usaron como parámetros genéricos. Esto último a su vez resulta en

ArrayList<Integer> li = new ArrayList<Integer>(); ArrayList<Float> lf = new ArrayList<Float>(); if(li.getClass() == lf.getClass()) // evaluates to true System.out.println("Equal");


Si escuchas Java Posse # 279 - Entrevista con Joe Darcy y Alex Buckley , hablan sobre este tema. Eso también se vincula a una publicación de Neal Gafter titulada Reified Generics for Java que dice:

Muchas personas no están satisfechas con las restricciones causadas por la forma en que se implementan los genéricos en Java. Específicamente, no están contentos de que los parámetros de tipo genérico no estén reificados: no están disponibles en tiempo de ejecución. Los genéricos se implementan mediante borrado, en el que los parámetros de tipo genérico simplemente se eliminan en tiempo de ejecución.

Esa publicación en el blog hace referencia a una entrada anterior, Puzzling Through Erasure: sección de respuestas , que enfatiza el punto sobre la compatibilidad de la migración en los requisitos.

El objetivo era proporcionar compatibilidad con versiones anteriores de código fuente y objeto, y también compatibilidad de migración.


Voy a arrojar una opinión realmente controvertida. Los genéricos complican el lenguaje y complican el código. Por ejemplo, supongamos que tengo un mapa que asigna una cadena a una lista de cadenas. En los viejos tiempos, podía declarar esto simplemente como

Map someMap;

Ahora, tengo que declararlo como

Map<String, List<String>> someMap;

Y cada vez que lo paso a algún método, tengo que repetir esa gran declaración larga otra vez. En mi opinión, todo ese tipeo extra distrae al desarrollador y lo saca de "la zona". Además, cuando el código se llena con muchos cruces, a veces es difícil volver a él más tarde y examinar rápidamente todo el fragmento para encontrar la lógica importante.

Java ya tiene una mala reputación por ser uno de los lenguajes más detallados de uso común, y los genéricos simplemente se suman a ese problema.

¿Y qué es lo que realmente compras para tanta verborrea? ¿Cuántas veces has tenido problemas en los que alguien puso un entero en una colección que se supone que contiene cadenas, o donde alguien intentó sacar una cadena de una colección de números enteros? En mis 10 años de experiencia trabajando en la construcción de aplicaciones comerciales de Java, esto nunca ha sido una gran fuente de errores. Por lo tanto, no estoy seguro de lo que obtendrás por la verborrea extra. Realmente me parece un bagaje burocrático extra.

Ahora voy a ser realmente polémico. Lo que veo como el mayor problema con las colecciones en Java 1.4 es la necesidad de encasillar en todas partes. Veo esos typecasts como extra, verbose cruft que tienen muchos de los mismos problemas que los genéricos. Entonces, por ejemplo, no puedo hacer

List someList = someMap.get("some key");

tengo que hacer

List someList = (List) someMap.get("some key");

La razón, por supuesto, es que get () devuelve un objeto que es un supertipo de lista. Entonces la tarea no se puede hacer sin un tipo de transmisión. Nuevamente, piense cuánto le cuesta realmente esa regla. Desde mi experiencia, no mucho.

Creo que Java habría estado mucho mejor si 1) no hubiera agregado genéricos, pero 2) hubiera permitido la conversión implícita de un supertipo a un subtipo. Deje que los moldes incorrectos sean atrapados en el tiempo de ejecución. Entonces podría haber tenido la simplicidad de definir

Map someMap;

y luego haciendo

List someList = someMap.get("some key");

todo el cruft se habría ido, y realmente no creo que estaría introduciendo una gran fuente nueva de errores en mi código.