¿Cuál es el concepto de borrado en generics en Java?

¿Cuál es el concepto de borrado en generics en Java?

Básicamente es la forma en que los generics se implementan en Java a través del engaño del comstackdor. El código genérico comstackdo realmente solo usa java.lang.Object siempre que hable de T (o de algún otro parámetro de tipo), y hay algunos metadatos que le dicen al comstackdor que realmente es un tipo genérico.

Cuando comstack un código contra un tipo o método genérico, el comstackdor resuelve lo que realmente quiere decir (es decir, cuál es el argumento de tipo para T ) y verifica en tiempo de comstackción que está haciendo lo correcto, pero el código emitido nuevamente solo habla en términos de java.lang.Object – el comstackdor genera conversiones adicionales cuando es necesario. En tiempo de ejecución, una List y una List son exactamente iguales; la información del tipo extra ha sido borrada por el comstackdor.

Compare esto con, digamos, C #, donde la información se retiene en el tiempo de ejecución, permitiendo que el código contenga expresiones como typeof(T) que es el equivalente a T.class , excepto que este último es inválido. (Hay más diferencias entre los generics .NET y los generics de Java, téngalo en cuenta). La borradura de tipo es la fuente de muchos de los mensajes de advertencia / error “extraños” cuando se trata de generics de Java.

Otros recursos:

  • Documentación de Oracle
  • Wikipedia
  • La guía de generics de Java de Gilad Bracha (PDF: muy recomendable; es posible que el enlace deba cambiar periódicamente)
  • Preguntas frecuentes sobre generics Java de Angelika Langer

Solo como una nota al margen, es un ejercicio interesante ver realmente lo que el comstackdor está haciendo cuando realiza el borrado, hace que todo el concepto sea un poco más fácil de entender. Hay un indicador especial que puede pasar el comstackdor para generar archivos java que tienen los generics borrados y los moldes insertados. Un ejemplo:

 javac -XD-printflat -d output_dir SomeFile.java 

El -printflat es la bandera que se -printflat al comstackdor que genera los archivos. (La parte -XD es lo que le dice a javac que se la entregue al archivo ejecutable que realmente hace la comstackción en lugar de solo javac , pero estoy divagando …) El -d output_dir es necesario porque el comstackdor necesita algún lugar para poner el nuevo. archivos java.

Esto, por supuesto, no solo borra; todas las cosas automáticas que hace el comstackdor aquí. Por ejemplo, los constructores por defecto también se insertan, el nuevo estilo foreach for bucles se expande a bucles regulares, etc. Es agradable ver las pequeñas cosas que están sucediendo automágicamente.

Para completar la respuesta ya muy completa de Jon Skeet, debe darse cuenta de que el concepto de borrado de tipo deriva de la necesidad de compatibilidad con versiones anteriores de Java .

Presentado inicialmente en EclipseCon 2007 (ya no está disponible), la compatibilidad incluía esos puntos:

  • Compatibilidad con la fuente (agradable de tener …)
  • Compatibilidad binaria (¡Debe tener!)
  • Compatibilidad de migración
    • Los progtwigs existentes deben continuar trabajando
    • Las bibliotecas existentes deben poder usar tipos generics
    • ¡Debe tener!

Respuesta original:

Por lo tanto:

 new ArrayList() => new ArrayList() 

Hay proposiciones para una mayor reificación . Reificar ser “Considerar un concepto abstracto como real”, donde los constructos del lenguaje deben ser conceptos, no solo azúcar sintáctico.

También debería mencionar el método checkCollection de Java 6, que devuelve una vista de seguridad dinámica de la colección especificada. Cualquier bash de insertar un elemento del tipo incorrecto dará lugar a una ClassCastException inmediata.

El mecanismo de generics en el lenguaje proporciona comprobación de tipos en tiempo de comstackción (estática), pero es posible vencer este mecanismo con conversiones no verificadas .

Por lo general, esto no es un problema, ya que el comstackdor emite advertencias en todas las operaciones no verificadas.

Sin embargo, hay momentos en los que la verificación de tipo estático por sí sola no es suficiente, como:

  • cuando una colección se pasa a una biblioteca de terceros y es imperativo que el código de la biblioteca no corrompa la colección insertando un elemento del tipo incorrecto.
  • un progtwig falla con una ClassCastException , lo que indica que un elemento incorrectamente escrito se puso en una colección parametrizada. Lamentablemente, la excepción puede ocurrir en cualquier momento después de insertar el elemento erróneo, por lo que normalmente proporciona poca o ninguna información sobre el origen real del problema.

Actualización de julio de 2012, casi cuatro años después:

Ahora está (2012) detallado en ” Reglas de compatibilidad de migración API (prueba de firma) ”

El lenguaje de progtwigción Java implementa generics mediante el borrado, lo que garantiza que las versiones heredadas y genéricas generalmente generen archivos de clase idénticos, excepto por cierta información auxiliar sobre los tipos. La compatibilidad binaria no se ha roto porque es posible reemplazar un archivo de clase heredado por un archivo de clase genérico sin cambiar ni recomstackr ningún código de cliente.

Para facilitar la interacción con el código heredado no genérico, también es posible usar la eliminación de un tipo parametrizado como un tipo. Tal tipo se llama tipo sin procesar ( Java Language Specification 3 / 4.8 ). Permitir el tipo sin procesar también garantiza la compatibilidad con versiones anteriores para el código fuente.

De acuerdo con esto, las siguientes versiones de la clase java.util.Iterator son compatibles tanto con el código fuente como con el código fuente:

 Class java.util.Iterator as it is defined in Java SE version 1.4: public interface Iterator { boolean hasNext(); Object next(); void remove(); } Class java.util.Iterator as it is defined in Java SE version 5.0: public interface Iterator { boolean hasNext(); E next(); void remove(); } 

Borrar, significa literalmente que la información de tipo que está presente en el código fuente se borra del bytecode comstackdo. Vamos a entender esto con un poco de código.

 import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class GenericsErasure { public static void main(String args[]) { List list = new ArrayList(); list.add("Hello"); Iterator iter = list.iterator(); while(iter.hasNext()) { String s = iter.next(); System.out.println(s); } } } 

Si comstack este código y luego lo descomstack con un decomstackdor Java, obtendrá algo como esto. Tenga en cuenta que el código descomstackdo no contiene ningún rastro de la información de tipo presente en el código fuente original.

 import java.io.PrintStream; import java.util.*; public class GenericsErasure { public GenericsErasure() { } public static void main(String args[]) { List list = new ArrayList(); list.add("Hello"); String s; for(Iterator iter = list.iterator(); iter.hasNext(); System.out.println(s)) s = (String)iter.next(); } } 

Complementando la respuesta Jon Skeet ya complementada …

Se ha mencionado que la implementación de generics a través del borrado conduce a algunas limitaciones molestas (por ejemplo, no new T[42] ). También se ha mencionado que la razón principal para hacer las cosas de esta manera era la compatibilidad con versiones anteriores en bytecode. Esto también es (mayormente) cierto. El bytecode generado -target 1.5 es algo diferente de solo casting desempañado -target 1.4. Técnicamente, incluso es posible (a través de inmensos engaños) acceder a instancias de tipo genérico en tiempo de ejecución , lo que demuestra que realmente hay algo en el bytecode.

El punto más interesante (que no se ha planteado) es que la implementación de los generics utilizando la eliminación ofrece bastante más flexibilidad en lo que puede lograr el sistema de tipo de alto nivel. Un buen ejemplo de esto sería la implementación de JVM de Scala frente a CLR. En la JVM, es posible implementar clases superiores directamente debido al hecho de que la propia JVM no impone restricciones en los tipos generics (ya que estos “tipos” están efectivamente ausentes). Esto contrasta con el CLR, que tiene conocimiento de tiempo de ejecución de instancias de parámetros. Debido a esto, la propia CLR debe tener algún concepto de cómo se deben usar los generics, anulando los bashs de extender el sistema con reglas imprevistas. Como resultado, los tipos superiores de Scala en el CLR se implementan utilizando una forma extraña de borrado emulada dentro del comstackdor, lo que los hace no totalmente compatibles con los generics simples .NET.

La eliminación puede ser un inconveniente cuando quieres hacer cosas malas en el tiempo de ejecución, pero ofrece la mayor flexibilidad para los escritores del comstackdor. Supongo que es parte de por qué no va a desaparecer pronto.

Según tengo entendido (siendo un tipo .NET ), la JVM no tiene ningún concepto de generics, por lo que el comstackdor reemplaza los parámetros de tipo con Object y realiza todo el casting por usted.

Esto significa que los generics de Java no son más que azúcar de syntax y no ofrecen ninguna mejora en el rendimiento para los tipos de valores que requieren boxeo / unboxing cuando se pasan por referencia.

Hay buenas explicaciones Solo agrego un ejemplo para mostrar cómo funciona el borrado de tipo con un decomstackdor.

Clase original

 import java.util.ArrayList; import java.util.List; public class S { T obj; S(T o) { obj = o; } T getob() { return obj; } public static void main(String args[]) { List list = new ArrayList<>(); list.add("Hello"); // for-each for(String s : list) { String temp = s; System.out.println(temp); } // stream list.forEach(System.out::println); } } 

Código descomstackdo de su bytecode,

 import java.io.PrintStream; import java.util.ArrayList; import java.util.Iterator; import java.util.Objects; import java.util.function.Consumer; public class S { Object obj; S(Object var1) { this.obj = var1; } Object getob() { return this.obj; } public static void main(String[] var0) { ArrayList var1 = new ArrayList(); var1.add("Hello"); // for-each Iterator iterator = var1.iterator(); while (iterator.hasNext()) { String string; String string2 = string = (String)iterator.next(); System.out.println(string2); } // stream PrintStream printStream = System.out; Objects.requireNonNull(printStream); var1.forEach(printStream::println); } } 

La progtwigción genérica es introducir en la versión 1.5 de java
Antes que nada, ¿qué es genérico en java?
La progtwigción genérica es un tipo de objeto seguro. Antes de la recostackción genérica, podemos almacenar cualquier tipo de objeto. y después de genérico debemos almacenar los datos del tipo específico de objeto.

¿Cuáles son las ventajas de Generic?
Las principales ventajas de genérico es que no se requiere conversión de tipo y también type-sage y Generic verificará el tiempo de comstackción. y la syntax general de Generic es ClassOrInterface aquí tipo es la señal de que esta clase puede haber establecido la clase cuando creó una instancia

Ejemplo . GenericClassDemo

genericclassDemo = new GenericClassDemo (Employee.java)