vectores vaciar una pueden numero matriz funcion eliminar elementos elemento dato cómo como borrar añadir arreglo java generics type-erasure

java - vaciar - ¿Cuándo se genera el valor de retorno genérico de la función después del borrado de tipo?



eliminar elemento en java (3)

Esta pregunta fue inducida por esta pregunta de StackOverflow sobre moldes inseguros: método de lanzamiento de Java sin saber a qué lanzar . Al responder a la pregunta que encontré este comportamiento, no pude explicarlo basándome únicamente en la especificación

Encontré la siguiente declaración en The Java Tutorials en los documentos de Oracle:

No se explica exactamente qué significa "si es necesario" , y no he encontrado ninguna mención sobre estos moldes en la Especificación del lenguaje Java , así que comencé a experimentar.

Veamos el siguiente fragmento de código:

// Java source public static <T> T identity(T x) { return x; } public static void main(String args[]) { String a = identity("foo"); System.out.println(a.getClass().getName()); // Prints ''java.lang.String'' Object b = identity("foo"); System.out.println(b.getClass().getName()); // Prints ''java.lang.String'' }

Compilado con javac y descompilado con Java Decompiler :

// Decompiled code public static void main(String[] paramArrayOfString) { // The compiler inserted a cast to String to ensure type safety String str = (String)identity("foo"); System.out.println(str.getClass().getName()); // The compiler omitted the cast, as it is not needed // in terms of runtime type safety, but it actually could // do an additional check. Is it some kind of optimization // to decrease overhead? Where is this behaviour specified? Object localObject1 = identity("foo"); System.out.println(localObject1.getClass().getName()); }

Puedo ver que hay un yeso que garantiza seguridad de tipo en el primer caso, pero en el segundo caso se omite. Está bien, por supuesto, porque quiero almacenar el valor de retorno en una variable de tipo Object , por lo que el molde no es estrictamente necesario según la seguridad del tipo. Sin embargo, conduce a un comportamiento interesante con los modelos inseguros:

public class Erasure { public static <T> T unsafeIdentity(Object x) { return (T) x; } public static void main(String args[]) { // I would expect c to be either an Integer after this // call, or a ClassCastException to be thrown when the // return value is not Integer Object c = Erasure.<Integer>unsafeIdentity("foo"); System.out.println(c.getClass().getName()); // but Prints ''java.lang.String'' } }

Compilado y descompilado, no veo ningún tipo de conversión para asegurar el tipo de devolución correcto en tiempo de ejecución:

// The type of the return value of unsafeIdentity is not checked, // just as in the second example. Object localObject2 = unsafeIdentity("foo"); System.out.println(localObject2.getClass().getName());

Esto significa que si una función genérica debe devolver un objeto de un tipo dado, no se garantiza que finalmente devolverá ese tipo. Una aplicación que use el código anterior fallará en el primer punto donde intente convertir el valor devuelto a un Integer si lo hace, así que siento que rompe el principio de no falla .

¿Cuáles son las reglas exactas del compilador que inserta este modelo durante la compilación que garantiza la seguridad del tipo y dónde se especifican esas reglas?

EDITAR:

Veo que el compilador no profundizará en el código y tratará de probar que el código genérico realmente devuelve lo que debería, pero podría insertar una aseveración, o al menos un tipo de molde (que ya lo hace en casos específicos, como se ve en el primer ejemplo) para garantizar el tipo de devolución correcto, por lo que este lanzaría una ClassCastException :

// It could compile to this, throwing ClassCastException: Object localObject2 = (Integer)unsafeIdentity("foo");


La versión 1 es preferible porque falla en tiempo de compilación .

Typesafe versión 1 código no heredado:

class Erasure { public static <T> T unsafeIdentity(T x) { //no cast necessary, type checked in the parameters at compile time return x; } public static void main(String args[]) { // This will fail at compile time and you should use Integer c = ... in real code Object c = Erasure.<Integer>unsafeIdentity("foo"); System.out.println(c.getClass().getName()); } }

Código heredado de la versión 2 de Typesafe ( error de tipo de tiempo de ejecución [...] en un modelo generado automáticamente para garantizar la validez de una operación en un tipo irrevocable y tipo de referencia ):

class Erasure { public static <T> T unsafeIdentity(Object x) { return (T) x; //Compiled version: return (Object) x; //optimised version: return x; } public static void main(String args[]) { // This will fail on return, as the returned Object is type Object and Subtype Integer is expected, this results in an automatic cast and a ClassCastException: Integer c = Erasure.<Integer>unsafeIdentity("foo"); //Compiled version: Integer c = (Integer)Erasure.unsafeIdentity("foo"); System.out.println(c.getClass().getName()); } }

Código heredado TypeSafe versión 3, Métodos donde se conoce un supertipo cada vez ( JLS El borrado de una variable de tipo (§4.4) es la eliminación de su límite más a la izquierda ):

class Erasure { public static <T extends Integer> T unsafeIdentity(Object x) { // This will fail due to Type erasure and incompatible types: return (T) x; // Compiled version: return (Integer) x; } public static void main(String args[]) { //You should use Integer c = ... Object c = Erasure.<Integer>unsafeIdentity("foo"); System.out.println(c.getClass().getName()); } }

El objeto solo se usó para ilustrar que Object es un objetivo de asignación válido en las versiones 1 y 3, pero debe usar el tipo real o el tipo genérico si es posible.

Si usa otra versión de java, debe mirar las páginas particulares de la especificación, no espero ningún cambio.


No puedo explicarlo muy bien, pero el comentario no puede agregar código tan bien como quiero, así que agrego esta respuesta. Solo espero que esta respuesta pueda ayudarlo a comprender. El comentario no puede agregar código tan bien como yo quiero.

En tu código:

public class Erasure { public static <T> T unsafeIdentity(Object x) { return (T) x; } public static void main(String args[]) { // I would expect it to fail: Object c = Erasure.<Integer>unsafeIdentity("foo"); System.out.println(c.getClass().getName()); // but Prints ''java.lang.String'' } }

Borrará genéricos después del tiempo de compilación. En tiempo de compilación, Erasure.unsafeIdentity no tiene errores. La eliminación de jvm Los genéricos dependen de los parámetros genéricos que proporcione (Entero). Después de eso, ¿la función es así ?:

public static Integer unsafeIdentity(Object x) { return x; }

De hecho, los retornos covariantes agregarán métodos puente :

public static Object unsafeIdentity(Object x) { return x; }

Si la función es como la última, ¿cree que el código de su método principal compilará? No tiene errores. Genéricos Erasure no agregará el molde en esta función, y los parámetros de retorno no son la identidad de la función java.

Mi explicación es un poco descabellada, pero la esperanza puede ayudarte a comprender.

Editar:

Después de google sobre ese tema, supongo que sus problemas son tipos de retorno covariantes utilizando métodos de puente. BridgeMethods


Si no puede encontrarlo en la especificación, eso significa que no está especificado, y depende de la implementación del compilador decidir dónde insertar los moldes o no, siempre que el código borrado cumpla con las reglas de seguridad tipo del código no genérico. .

En este caso, el código borrado del compilador se ve así:

public static Object identity(Object x) { return x; } public static void main(String args[]) { String a = (String)identity("foo"); System.out.println(a.getClass().getName()); Object b = identity("foo"); System.out.println(b.getClass().getName()); }

En el primer caso, el yeso es necesario en el código borrado, porque si lo eliminaste, el código borrado no se compilaría. Esto se debe a que Java garantiza que lo que se mantiene en el tiempo de ejecución en una variable de referencia de tipo reificable debe ser instanceOf ese tipo confiable, por lo que aquí es necesaria una verificación en tiempo de ejecución.

En el segundo caso, el código borrado se compila sin un molde. Sí, también compilará si agregó un molde. Entonces el compilador puede decidir de cualquier manera. En este caso, el compilador decidió no insertar un molde. Esa es una elección perfectamente válida. No debe confiar en el compilador para decidir de una u otra forma.