Introducción a genéricos en Java
Java 5.0 introdujo varias herramientas nuevas a Java. Una de ellas fue la introducción de los genéricos.
Comenzaremos con una introducción a los genéricos. Los genéricos nos permiten abstraer sobre tipos. Los ejemplos más comunes son los tipos contenedores, como los de la jerarquía Collections
. Antes de los genéricos, las colecciones de Java solo podían contener objetos de tipo Object
. Esto significaba que, cuando se recuperaba un objeto de una colección, era necesario hacer un casteo (cast) explícito al tipo correcto. Los genéricos eliminan la necesidad de este casteo, proporcionando una forma de especificar el tipo de datos que una colección puede contener.
Veamos un ejemplo. Supongamos que tenemos una lista de enteros. El código es el siguiente:
1List miListaEnteros = new LinkedList(); // 12miListaEnteros.add(new Integer(0)); // 23Integer x = (Integer) miListaEnteros.iterator().next(); // 3
El casteo (cast) en la línea 3 es algo poco molesto. Típicamente, uno sabe qué tipo de datos se ha colocado en una lista en particular. Sin embargo, el casteo es esencial. El compilador solo puede garantizar que un Object
será devuelto por el iterador. Para asegurar que la asignación a una variable de tipo Integer
sea segura en cuanto a tipos, se requiere el casteo.
Por supuesto, el casteo no solo introduce código molesto, sino que también introduce la posibilidad de un error en tiempo de ejecución, ya que el programador puede cometer errores.
¿Qué pasaría si pudiéramos realmente expresar nuestra intención y marcar una lista como restringida para contener un tipo de datos particular? Esta es la idea principal detrás de los genéricos. Ahora veamos una versión genérica del código anterior:
1List<Integer> miListaEnteros = new LinkedList<Integer>(); // 12miListaEnteros.add(new Integer(0)); // 23Integer x = miListaEnteros.iterator().next(); // 3
Observemos la declaración de tipo para la variable miListaEnteros
. Esto especifica que esto no es simplemente una List
arbitraria, sino una List
de Integer
, escrita como List<Integer>
. Decimos que List
es una interfaz genérica que toma un parámetro de tipo, en este caso, Integer
. También especificamos un parámetro de tipo al crear el objeto.
Observemos también que el casteo de la línea 3 ha desaparecido.
Ahora, se podría pensar que todo lo que hemos logrado es mover el código molesto a otro lugar. En lugar de un casteo a Integer
en la línea 3, tenemos Integer
como un parámetro de tipo en la línea 1. Sin embargo, hay una gran diferencia aquí. El compilador ahora puede verificar la corrección de tipos del programa en tiempo de compilación. Cuando decimos que miListaEnteros
se declara con tipo List<Integer>
, esto nos da información adicional sobre la variable miListaEnteros
, que es cierta dondequiera y cuandoquiera que se use, y el compilador lo garantizará. En contraste, el casteo nos dice algo que el programador piensa que es cierto en un solo punto del código.
En las siguientes secciones, exploraremos más a fondo cómo los genéricos pueden ayudar a hacer que nuestro código sea más seguro y más fácil de leer.