tipo son que programacion los genericos generico generica elementos clase beneficios array java generics

programacion - que son generics java



¿Por qué este uso de Generics no lanza una excepción de tiempo de ejecución o de compilación? (3)

Tengo un método en una clase que tiene un tipo de retorno especificado por el uso de un genérico.

public class SomeMain { public static void main(String[] args) { Foo<Integer> foo = new Foo<Integer>(); System.out.println(foo.getFoo()); // Works, prints out "Foo" } public static class Foo<E> { public E getFoo() { return (E) "Foo"; } } }

Con el tipo de retorno genérico, asumí que el retorno en el ejemplo anterior se evaluaría para:

return (Integer) "Foo"; // Inconvertible types !!!

En su lugar, una String se devuelve e imprime correctamente.

Recibo un error de compilación si cambio la llamada a ser:

String fooString = foo.getFoo(); // Compile error, incompatible types found System.out.println(fooString);

¿Qué es lo que me falta para ayudarme a comprender lo que está pasando aquí y por qué la versión original no produjo un error de compilación?


Esto se debe a que la resolución de sobrecarga resolvió su llamada a println(Object) , ya que no hay println(Integer) .

Tenga en cuenta que los genéricos de Java se borran en tiempo de ejecución. Y los lanzamientos como (E) "Foo" se eliminan y se mueven para llamar al sitio. A veces esto no es necesario, por lo que las cosas se lanzan al tipo correcto solo cuando es necesario.

En otras palabras, no se realizan getFoo dentro de getFoo . La especificación de idioma soporta esto:

Sección 5.5.2 Fundiciones revisadas y no comprobadas

  • El elenco es un elenco completamente sin marcar.

    No se realiza ninguna acción en tiempo de ejecución para tal lanzamiento.

Después de borrar, getFoo devuelve Object . Y eso se pasa a println(Object) , que está perfectamente bien.

Si llamo a este método y paso foo.getFoo , obtendré un error:

static void f(Integer i) { System.out.println(i); } // ... f(foo.getFoo()); // ClassCastException

porque esta vez necesita ser fundido.


Me gustaría agregar que durante el proceso de borrado de tipo , el compilador de Java reemplaza el parámetro de tipo ilimitado E con Object , por lo tanto, la clase Foo se compila en:

public static class Foo { public Object getFoo() { return "Foo"; } }

Es por eso que el siguiente código es válido (no se necesita cast):

Object obj = foo.getFoo(); System.out.println(obj);

Al mismo tiempo, el siguiente fragmento de código produce un error en tiempo de compilación, como se esperaba:

Foo<Integer> foo = new Foo<Integer>(); String fooString = foo.getFoo(); // you''re trying to trick the compiler (unsuccessfully) ^ incompatible types: Integer can not be converted to String

Y esa es la principal responsabilidad de los genéricos : los controles de tiempo de compilación.

Sin embargo, hay otra cara de la historia: los moldes en tiempo de ejecución. Por ejemplo, si escribes:

Integer value = foo.getFoo();

obtienes una ClassCastException lanzada en tiempo de ejecución (el compilador de Java inserta una instrucción de checkcast que examina si el resultado de foo.getFoo() se puede convertir a Integer ).


System.out.println no tiene una sobrecarga que tome Integer . Así que esta declaración:

System.out.println(foo.getFoo());

Está llamando System.out.println(Object); .

Para verificar que de lo contrario fallaría, intente:

Foo<Integer> foo = new Foo<Integer>(); Integer fooInt = foo.getFoo(); //class cast exception

Lo siguiente fallará de la misma manera:

public static void main(String[] args) throws Exception { Foo<Integer> foo = new Foo<Integer>(); print(foo.getFoo()); //Would also fail with a class cast exception } static void print(Integer in) { System.out.println(in); }

Y esta es una compilación fallida por razones obvias:

String fooString = foo.getFoo(); //can''t work

foo es Foo<Integer> , y foo.getFoo() devuelve un Integer y el compilador puede recogerlo.