intive Argentina Blog

Cómo utilizar generics en Java

Los generics llegaron al mundo para darnos una gran mano a los programadores Java. Antes de su creación, utilizábamos colecciones, lo que hacía que en la ejecución de los programas aparecieran varios errores. Ahora que podemos utilizar los generics para parametrizar tipos (clases e interfaces), nos encontramos con un nuevo panorama. Los generics nos permiten manipular y operar con objetos sin especificar su tipo y posibilitan reutilizar código, además de ofrecer type safety, gracias al chequeo de tipos durante la compilación.

En este nuevo universo que abrieron los generics, también es posible crear métodos genéricos, agregándoles distintos parámetros con tipos genéricos (esto es similar a declarar un tipo genérico, pero su alcance se limita al método en el que se declaró). Además, una clase genérica puede tener múltiples parámetros (multiple type parameters). Dichos parámetros con tipo (aquellos que son un marcador determinado por quien encarga un trabajo en la creación de una variable de tipo genérico) pueden ser reemplazados por argumentos con tipo (parameterized types). Un modo de hacerlo es el siguiente: Box<Integer>. Otra opción es crear una clase genérica con argumentos sin tipo (raw types). Por ejemplo: Box<T>.

 

Convención de nombres

Dado que se trata de tipos genéricos, su nomenclatura no afecta su comportamiento y podría designarse cualquier nombre para un genérico en Java. Sin embargo, existen convenciones para los nombres de estos tipos cuyo objetivo es mejorar la legibilidad e interpretación del código. Algunas de importancia son:

  • E – Element.
  • K – Key.
  • V – Value.
  • N – Number.
  • T – Type.
  • S, U, V, y así sucesivamente, para más tipos.

 

Ventajas (y desventaja)

Entre los pros de utilizar generics, podemos destacar:

  • Permite la reutilización de código: es posible utilizar el mismo código para tipos diferentes.
  • Evita el casteo de clases (por casteo entendemos realizar cambios entre tipos de datos distintos, donde chocan tipos de valores de datos).
  • Código de mayor calidad, legible y limpio, si se utilizan las convenciones con criterio.
  • Type safety: código más seguro, ya que reduce las oportunidades de que se introduzca un error.
  • Rapidez de ejecución, dado que no hay chequeo de tipos en este tiempo. El casteo, en cambio, requiere que la JVM haga chequeo de tipos en tiempo de ejecución, en caso de que sea necesario ejecutar una ClassCastException.
  • Chequeo de tipos en tiempo de compilación: el compilador verifica que se utilice la clase genérica donde se la invoca; en cambio, no lo hace cuando se invoca al tipo Object.
  • Como contra, podemos señalar cierta dificultad en la interpretación del código si no se utilizan los nombres con claridad o no se cuenta con documentación.

Como contra, podemos señalar cierta dificultad en la interpretación del código si no se utilizan los nombres con claridad o no se cuenta con documentación.

 

Type erasure

Cuando se utilizan generics, se realizan casteos y comprobaciones de tipos en tiempo de compilación, los cuales tienen en cuenta la información almacenada en metadata para los tipos de generics. Por lo tanto, luego de que el compilador chequea las validaciones necesarias, elimina esa metadata en su totalidad. A este proceso de eliminación se lo denomina type erasure o borrado de tipos; no existe información de tipos en tiempo de ejecución y el uso de tipos para los generics se valida en tiempo de compilación.

 

Bounds

Incluso utilizando generics, a veces se presentan ciertas restricciones respecto de los tipos, teniendo que usar -en determinados casos- tipos concretos. Por ejemplo, en el caso de que una función aplique sólo a números, se utilizan los tipos límites o bounded types.

Con estos tipos, es posible limitar a los generics haciendo que se extiendan de una clase y en consecuencia, que su límite sea el de ese tipo.

Además, los bounds, en generics, pueden ser múltiples (límites múltiples). Estos representan la herencia múltiple de interfaces de Java para generics y, al igual que en la herencia común, es posible extender sólo de una clase y de varias interfaces.

La forma de utilizarlos es <T extends A & B1 & B2 & B3>. El orden en el que se colocan los límites es importante: si se extiende de una clase y de una -o varias- interfaces, la clase debe colocarse primero y luego las interfaces ya que, en caso contrario, habrá un error en la compilación. En este ejemplo, A sería una clase y Bn las interfaces.

Los bounds pueden utilizarse en el parámetro de una clase y en los parámetros que recibe y devuelve un método. En el caso del parámetro de una clase es importante resaltar que, por ejemplo, List<A> es distinta a List<B>, aunque A se extienda de B. De la misma manera, Box<Number> es distinto de Box<Integer>.

 

Wildcard types

Los tipos comodines o wildcard types en generics se utilizan para designar un tipo desconocido que puede ser de cualquier clase y será superclase para todas las demás. Se indica con el signo de interrogación “?”. Por ejemplo: Collection<?>.

Pueden tener upper bounds o lower bounds para indicar que son subclases o superclases de otra clase y se indica de la siguiente forma: <? extends someClass>, para indicar de qué clase extiende y <? super someClass> para mostrar qué clase es superclase.

A diferencia de los casos anteriores, al utilizar estos tipos comodines, debemos tener en cuenta que, siguiendo el ejemplo de arriba, si bien Box<Number> es distinto de Box<Integer>, ambos extienden de Box<?>. Lo mismo ocurriría con las listas: List<A> y List<B> extienden de List<?>.

 

Ejemplo

Para poder visualizar y entender mejor lo que venimos hablando a lo largo del artículo, les proponemos una clase completa con un tipo genérico:

Además, por si desean más información, dejamos algunos links de referencia.

Happy coding!

Julia Mendiola

Es desarrolladora de software en intive – FDV desde diciembre de 2015 e integra la Brigada Java. Es Licenciada en Sistemas egresada de la Universidad Nacional del Noroeste de la Provincia de Buenos Aires. En 2014 fue reconocida por el Honorable Concejo Deliberante de Junín -ciudad donde nació- con la mención “Joven Destacado” por participar en el desarrollo de un teclado simple (Android) para personas con discapacidades motrices.

Deja un comentario