parameter - java generic type inheritance
¿Por qué es posible recuperar un objeto de "tipo incorrecto" de la Lista parametrizada en Java? (5)
Aquí hay un fragmento de código:
import java.util.*;
class Test
{
public static void main(String[] args)
{
List<Integer> list = new ArrayList<>();
addToList(list);
Integer i = list.get(0); //#1 fails at run-time
String s = list.get(0); //#2 fails at compile-time
list.get(0); //#3 works fine
System.out.println(list.get(0)); //#4 works fine, prints "string"
}
static void addToList(List list){
list.add("string");
}
}
Entiendo por qué es posible insertar un objeto de clase String en Lista parametrizada.
Parece que entiendo por qué el código marcado con los #1
y #2
falla.
Pero, ¿por qué funcionan los #3
y #4
? Por lo que yo entiendo, el compilador agrega moldes apropiados después del borrado de tipos, por lo que cuando llamo a list.get(0)
, este método debe devolver un Objeto previamente enviado a Entero. Entonces, ¿por qué no hay ClassCastException en el # 3 y # 4 en el tiempo de ejecución?
4: se está llamando a la sobrecarga System.out.println (Object), ya que Integer <= _ T Object (leer: Integer is-a Object). Tenga en cuenta que list.get (int) devuelve un objeto en tiempo de ejecución ya que el parámetro de tipo se borra. Ahora lee
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
que le indica "Insertar yesos de tipo si es necesario para preservar la seguridad del tipo". Dado que no se necesita un molde de tipo de Objeto a Objeto, no puede dar lugar a ClassCastException.
Por la misma razón, no hay ningún tipo de conversión en 3, sin embargo, los efectos secundarios de la llamada al método podrían ocurrir, de los cuales List.get no tiene ninguno.
El # 3 funciona porque el objeto devuelto por get(int)
se ignora. Lo que se almacena en la posición 0
se devuelve, pero dado que no hay conversión, no ocurre ningún error.
El # 4 funciona bien por el mismo motivo: el objeto producido por get(0)
se trata como subclase java.lang.Object
en println
, porque se llama a toString
. Dado que toString()
está disponible para todos los objetos Java, la llamada se completa sin un error.
Los moldes se aplican al tipo de devolución de get
, no al add
que está introduciendo la contaminación. De lo contrario, obtendría un error en tiempo de compilación o una excepción en ese punto, ya que no puede convertir un String
en un Integer
.
Primero, la razón por la que puede agregar una cadena a una List<Integer>
. En el método
static void addToList(List list){
usas un tipo crudo Los tipos en bruto existen puramente para compatibilidad con las versiones anteriores de Java y no deberían utilizarse en el nuevo código. Dentro del método addToList
, el compilador de Java no sabe que la list
solo debe contener enteros y, por lo tanto, no se queja cuando se le agrega una cadena.
En cuanto al comportamiento diferente de ustedes dos declaraciones. Integer i = list.get(0)
no falla en el momento de la compilación, porque Java piensa que esa list
solo contiene Integer
. Solo en tiempo de ejecución resulta que el primer elemento de la list
no es un Entero y, por lo tanto, obtienes una ClassCastException
.
String s = list.get(0)
falla en tiempo de compilación porque el compilador de Java asume que esa list
solo contiene enteros, y asume que intentas asignar un entero a una referencia de cadena.
Simplemente list.get(0)
no almacena el resultado de la llamada al método. Por lo tanto, ni en tiempo de compilación ni en tiempo de ejecución hay ninguna razón para una falla.
Finalmente, System.out.println(list.get(0))
funciona porque System.out
es un PrintStream
y tiene un println(Object)
, que se puede println(Object)
con un argumento Integer
.
Si miras ArrayList#get
método ArrayList#get
. Es esto:
public E get(int index) {
//body
}
Pero en tiempo de ejecución es en realidad:
public Object get(int index) {
//body
}
Entonces cuando haces Integer i = list.get(0);
El compilador lo convierte en:
Integer i = (Integer)list.get(0);
Ahora, en tiempo de ejecución, list.get(0)
devuelve un tipo de Object
(que en realidad es String
). Ahora intenta convertir String => Integer
y falla.
3
Porque es justo:
list.get(0)
El compilador agrega typecasting a cualquier cosa. Entonces es justo, list.get(0)
.
4
System.out.println(list.get(0));
list.get(0)
devuelve un tipo de Object
. Entonces se llama al método public void println(Object x)
de PrintStream.
Recuerde println (Object x) se llama y no println (String x) .