Tipos genéricos Sólo compilación Compatibilidad Parchando
Tipos genéricos
en Java
Verónica E. Arriola-Rios
Facultad de Ciencias, UNAM
20 de enero de 2021
Verónica E. Arriola-Rios Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Temas
1 Tipos genéricos
Qué son los tipos genéricos
Métodos genéricos
Subtipos
2 Los genéricos ayudan únicamente en tiempo de compilación
Borrado de tipo
Donde el asunto puede ir muy mal
3 Compatibilidad
Interactuando con código viejo
*Del pasado al presente, que salga equivalente
(o actualizando código viejo)
4 Parchando
Verónica E. Arriola-Rios Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Tipos genéricos
1 Tipos genéricos
2 Los genéricos ayudan únicamente en tiempo de compilación
3 Compatibilidad
4 Parchando
Verónica E. Arriola-Rios Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Temas
1 Tipos genéricos
Qué son los tipos genéricos
Métodos genéricos
Subtipos
Verónica E. Arriola-Rios Definición Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Genéricos
Proveen un mecanismo para crear métodos y estructuras que funcionan sobre
tipos distintos sin necesidad de utilizar conversión explícita (casting).
Permiten detectar un mayor número de errores en tiempo de compilación, en lugar
de en tiempo de ejecución[1] .
En Java los tipos genéricos funcionan exclusivamente en tiempo de
compilación.
[1]
Siempre y cuando no se abuse de ellos.
Verónica E. Arriola-Rios Definición Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Ejemplo de uso
Antes
1 Lista l = new Lista () ;
2 l . agrega ( new Numero () ) ;
3 Object o = l . lee (0) ;
4 Numero n = ( Numero ) l . lee (0) ;
vs
Ahora
1 Lista < Numero > l = new Lista < >() ;
2 l . agrega ( new Numero () ) ;
3 Numero n = l . lee (0) ;
Verónica E. Arriola-Rios Definición Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Ejemplo de declaración
1 public class Lista <E > {
2 private E dato ;
3 private Lista <E > sig ;
4
5 public Lista ( E dato ) {
6 this . dato = dato ;
7 }
8
9 public void agrega ( E dato ) {
10 if ( sig != null ) sig . agrega ( dato ) ;
11 else {
12 // Usando el diamante <>
13 sig = new Lista < >( dato ) ;
14 }
15 }
16 }
Verónica E. Arriola-Rios Definición Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Convensiones de nombrado
Se utiliza una sola letra mayúscula.
Nombres comunes son:
E elemento (almacenado en una estructura de datos).
K llave en un mapa o diccionario (del inglés key ).
N número
T tipo (cualquiera)
V valor
S,U,V,... cuando se requieren más variables.
Verónica E. Arriola-Rios Definición Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Las cosas que no se pueden hacer
No se puede:
1 asociar tipos primitivos a variables genéricas (pero se pueden usar wrappers),
2 crear objetos a partir de parámetros de tipo,
3 crear campos estáticos con tipos genéricos,
4 *usar conversiones de tipo o usar instanceof con tipos parametrizados,
5 crear arreglos de tipos parametrizados,
6 crear, cachar o lanzar objetos de tipos parametrizados,
7 sobrecargar métodos donde los parámetros formales de tipos se borren a los
mismos tipos.
Verónica E. Arriola-Rios Definición Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Temas
1 Tipos genéricos
Qué son los tipos genéricos
Métodos genéricos
Subtipos
Verónica E. Arriola-Rios Métodos Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Métodos genéricos
Si requerimos utilizar el tipo del argumento:
Es posible declarar y utilizar una variable genérica que se utilice únicamente en el
método:
1 public static <T > void f romAr rayT oCol lecti on
2 ( T [] a , Collection <T > c ) {
3 for ( T o : a ) {
4 c . add ( o ) ; // Correcto
5 }
6 }
Verónica E. Arriola-Rios Métodos Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Temas
1 Tipos genéricos
Qué son los tipos genéricos
Métodos genéricos
Subtipos
Verónica E. Arriola-Rios Subtipos Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Contenedores de subtipos vs subtipos de contenedores
¿Qué sucede si queremos utilizar subtipos[2] y genéricos?
x Un contenedor de tipo T no es lo mismo que un contenedor de supertipos de T,
pues el contenedor de supertipos puede contener elementos que el otro no.
1 Lista < String > l = new Lista < >() ;
2 Lista < Object > lo = ls ; // Error
3 lo . agrega ( new Object () ) ;
[2]
Herencia de clases e implementación de interfaces
Verónica E. Arriola-Rios Subtipos Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Comodines
Para crear métodos que funcionen con varias clases con tipos genéricos, se
requieren comodines (wildcards).
1 public void printIterable ( Iterable <? > c ) {
2 for ( Object e : c ) {
3 System . out . println ( e ) ;
4 }
5 }
Verónica E. Arriola-Rios Subtipos Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Comodines acotados superiormente
Si queremos limitar los tipos que pueden ser aceptados por un método utilizamos
comodines acotados.
Cota superior (una superclase):
1 public Real sumaTodos
2 ( Iterable <? extends Real > numeros ) {
3 Real r = new Doble (0) ;
4 for ( Numero n : numeros ) {
5 r = r . suma ( n ) ;
6 }
7 return r ;
8 }
¡OJO! Dado que se desconoce el tipo exacto de numeros ahora el compilador
prohibe utilizar aquellos métodos que requieran conocer el tipo genérico exacto,
por ej. public void agrega(T e).
Verónica E. Arriola-Rios Subtipos Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Cotas múltiples
1 Class A { /* ... */ }
2 interface B { /* ... */ }
3 interface C { /* ... */ }
4
5 class D <T extends A & B & C > { /* ... */ }
La clase de la cual se debe heredar debe ser la primera en ser especificada.
Verónica E. Arriola-Rios Subtipos Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Otro ejemplo con cotas superiores:
1 public class Collections {
2 public static <T > void copy
3 ( List <T > dest , List <? extends T > src ) {
4 ...
5 }
6 }
Verónica E. Arriola-Rios Subtipos Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Comodines acotados inferiormente
Cota inferior. Por ejemplo, nos sirve para métodos genéricos que escriben.
1 interface Sink <T > {
2 flush ( T t ) ;
3 }
4 public static <T > T writeAll ( Collection <T > coll ,
5 Sink <? super T > snk ) {
6 T last ;
7 for ( T t : coll ) {
8 last = t ;
9 snk . flush ( last ) ;
10 }
11 return last ;
12 }
13 Sink < Object > s ; // ? es un supertipo de T
14 Collection < String > cs ;
15 String str = writeAll ( cs , s ) ; // Yes !
Verónica E. Arriola-Rios Subtipos Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Los genéricos ayudan únicamente en tiempo de compilación
1 Tipos genéricos
2 Los genéricos ayudan únicamente en tiempo de compilación
3 Compatibilidad
4 Parchando
Verónica E. Arriola-Rios Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Temas
2 Los genéricos ayudan únicamente en tiempo de compilación
Borrado de tipo
Donde el asunto puede ir muy mal
Verónica E. Arriola-Rios Borrado de tipo Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Borrado de tipo (Type erasure)
Las variables genéricas existen únicamente en tiempo de compilación.
Se puede ver como si el código fuente fuera convertido a otro código fuente sin
genéricos donde:
T es reemplazado por Object, T extends Número es reemplazado por Número y T
super Número es reemplazado por Object.
Los castings requeridos son insertados automáticamente.
1 public class ConDato <T extends Entero > { 1 public class ConDato {
2 private T datoT ; 2 private Entero datoT ;
3 public T get () { 3 public Entero get () {
4 return datoT ; 4 return ( Entero ) datoT ;
5 } 5 }
6 } 6 }
Se crean métodos puente para preservar el polimorfismo entre clases genéricas
extendidas.
Verónica E. Arriola-Rios Borrado de tipo Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Código 2: Después del borrado de tipos
Código 1: Código con genéricos 1 public class Node {
2 public Object data ;
1 public class Node <T > {
3 public Node ( Object data ) { this . data = data ; }
2 public T data ;
4 public void setData ( Object data ) {
3 public Node ( T data ) { this . data = data ; }
5 System . out . println ( " Node . setData " ) ;
4 public void setData ( T data ) {
6 this . data = data ;
5 System . out . println ( " Node . setData " ) ;
7 }
6 this . data = data ;
8 }
7 }
9
8 }
10 public class MyNode extends Node {
9
11 public MyNode ( Integer data ) { super ( data ) ; }
10 public class MyNode extends Node < Integer > {
12 // Bridge method g e n e r a t e d by the compiler
11 public MyNode ( Integer data ) { super ( data ) ;
13 public void setData ( Object data ) {
–> }
14 setData (( Integer ) data ) ;
12 public void setData ( Integer data ) {
15 }
13 System . out . println ( " MyNode . setData " ) ;
16 public void setData ( Integer data ) {
14 super . setData ( data ) ;
17 System . out . println ( " MyNode . setData " ) ;
15 }
18 super . setData ( data ) ;
16 }
19 }
20 }
Verónica E. Arriola-Rios Borrado de tipo Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Si los tipos genéricos fueron borrados...
En tiempo de ejecución:
Sólo quedan los tipos ordinarios.
1 List < String > l1 = new ArrayList < String >() ;
2 List < Integer > l2 = new ArrayList < Integer >() ;
3 System . out . println
4 ( l1 . getClass () == l2 . getClass () ) ; // true
No podemos usar instanceof con genéricos:
1 java . util . Collection cs = new ArrayList < String >() ;
2 // Illegal .
3 if ( cs instanceof java . util . Collection < Integer >) {
4 System . out . print ( " Yes ␣ it ␣ is " ) ;
5 }
6 // Legal
7 if ( cs instanceof java . util . Collection ) {
8 System . out . print ( " Yes ␣ it ␣ is " ) ;
9 }
Verónica E. Arriola-Rios Borrado de tipo Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Temas
2 Los genéricos ayudan únicamente en tiempo de compilación
Borrado de tipo
Donde el asunto puede ir muy mal
Verónica E. Arriola-Rios Donde el asunto puede ir muy mal Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Regla Golem #2
Se supone que esto lanza una advertencia (el compilador puede estar configurado
para no lanzarla):
1 Lista < String > cstr = ( Lista < String >) cs ;
1 <T > T badCast ( T t , Object o ) {
2 return ( T ) o ;
3 }
Verónica E. Arriola-Rios Donde el asunto puede ir muy mal Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Infiltrando al enemigo: contaminación del heap
1 import java . util . ArrayList ;
2 import java . util . LinkedList ; // Can trick too
3
4 public class ArrayListTrick {
5
6 /* * Let ’s errase the identity of any object . */
7 public static <T ,U > T deceiver ( U something ) {
8 return ( T ) something ;
9 }
10
11 /* * Abusing the type erasure system ... */
12 public static void main ( String [] args ) {
13 // Only numbers
14 ArrayList < Number > lista = new ArrayList < >() ;
15 // LinkedList < Number > lista = new LinkedList < >() ;
16 String intruder = " Hola " ;
17 // ArrayList stores its stuff inside an Object [] array .
18 lista . add ( deceiver ( intruder ) ) ; // T is cleaverly inferred to be "
–>Number ".
19 lista . add ( new Double (24.5) ) ;
20 lista . add ( new Float (50.3) ) ;
21 lista . add ( new Integer (100) ) ;
22 for ( int i = 0; i < lista . size () ; i ++) {
23 // OK , just don ’t make it explicit there is an intruder there .
24 // If I call anything specific to Number someone will figure out
–>.
25 System . out . println ( lista . get ( i ) ) ;
26 }
27 // The compiler won ’t let me cast a Number into String , so let ’s
–>use a trick .
28 String cameBackAlive = deceiver ( lista . get (0) ) ;
29 System . out . println ( cameBackAlive + " ␣ made ␣ it ␣ back ! " ) ;
30 }
31 }
Verónica E. Arriola-Rios Donde el asunto puede ir muy mal Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Infiltrando al enemigo (¡descubierto!)
1 import java . util . ArrayList ;
2 import java . util . LinkedList ; // Can trick too
3
4 public class ArrayListTrick {
5
6 /* * Let ’s errase the identity of any object . */
7 public static <T ,U > T deceiver ( U something ) {
8 return ( T ) something ;
9 }
10
11 /* * Abusing the type erasure system ... */
12 public static void main ( String [] args ) {
13 // Only numbers
14 ArrayList < Number > lista = new ArrayList < >() ;
15 // LinkedList < Number > lista = new LinkedList < >() ;
16 String intruder = " Hola " ;
17 // ArrayList stores its stuff inside an Object [] array .
18 lista . add ( deceiver ( intruder ) ) ; // T is cleaverly inferred to be "
–>Number ".
19 lista . add ( new Double (24.5) ) ;
20 lista . add ( new Float (50.3) ) ;
21 lista . add ( new Integer (100) ) ;
22 Number n = 0;
23 for ( int i = 0; i < lista . size () ; i ++) {
24 // Oops !!!
25 n = n . doubleValue () + lista . get ( i ) . doubleValue () ;
26 }
27 // The compiler won ’t let me cast a Number into String , so let ’s
–>use a trick .
28 String cameBackAlive = deceiver ( lista . get (0) ) ;
29 System . out . println ( cameBackAlive + " ␣ made ␣ it ␣ back ! " ) ;
30 }
31 }
Verónica E. Arriola-Rios Donde el asunto puede ir muy mal Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Regla Golem #3. Arreglos
El tipo de los objetos almacenados en un arreglo no puede ser:
Un tipo genérico (ej. T).
T[] a = new T[100]; x
Un tipo parametrizado (ej. Lista<T>).
List<String>[] lsa = new List<String>[10]; x
a menos que sea un comodín no acotado ?:
List<?>[] lsa = new List<?>[10]; X
Verónica E. Arriola-Rios Donde el asunto puede ir muy mal Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Regla Golem #3. Arreglos (continuación)
¡Sí se pueden declarar estos tipos, pero no crear esos objetos!
Permite pasar arreglos como parámetros, sin que se conozca su tipo.
1 ...
2 public static <T > void imprime ( T [] arreglo ) {
3 for ( T o : arreglo ) System . out . println ( o ) ;
4 }
5 public static void main ( String [] args ) {
6 Integer [] is = {1 ,2 ,3 ,4 ,5};
7 imprime ( is ) ;
8 }
9 ...
Esta regla tienta a que se hagan conversiones explícitas, pero no impide el posible
error en tiempo de ejecución.
x Se presta a usos con consecuencias impredecibles como:
T[] a = (T[]) new Object[10];
Verónica E. Arriola-Rios Donde el asunto puede ir muy mal Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Compatibilidad
1 Tipos genéricos
2 Los genéricos ayudan únicamente en tiempo de compilación
3 Compatibilidad
4 Parchando
Verónica E. Arriola-Rios Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Temas
3 Compatibilidad
Interactuando con código viejo
*Del pasado al presente, que salga equivalente
(o actualizando código viejo)
Verónica E. Arriola-Rios Código viejo Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Tipos puros (raw types)
Si no se especifica el tipo actual de una clase genérica, se dice que el tipo es
puro [3] .
Un tipo puro se refiere a cualquier genérico, como si los genéricos no hubieran
existido.
Cada declaración en el código siguiente significa algo distinto:
1 Lista l ; // Tipo puro
2 Lista < Numero > ln ;
3 Lista < Object > lo ;
4 Lista <? > lq ;
[3]
En inglés raw type.
Verónica E. Arriola-Rios Código viejo Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Regla Golem #1
Los tipos puros permiten interactuar con código viejo realizando asignaciones
inseguras como:
1 public interface Assembly {
2 // Returns a collection of Parts
3 Collection getParts () ;
4 }
5 ...
6 Collection < Part > k =
7 Inventory . getAssembly ( " thingee " ) . getParts () ;
“Shouldn’t this be an error? Theoretically speaking, yes; but practically
speaking, if generic code is going to call legacy code, this has to be allowed.”
Verónica E. Arriola-Rios Código viejo Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Temas
3 Compatibilidad
Interactuando con código viejo
*Del pasado al presente, que salga equivalente
(o actualizando código viejo)
Verónica E. Arriola-Rios Actualizando Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Cotas múltiples
Si se quiere mantener la compatibilidad con otras clases el código nuevo debe:
Funcionar para los mismos argumentos y tipos de regreso.
Mantener compatibilidad a nivel binario (debe seguir ejecutando correctamente con
otro código compilado).
La regla general es: al borrar los tipos genéricos debe quedar la fima del método
original sin genéricos.
Verónica E. Arriola-Rios Actualizando Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Borrando a la firma anterior
Por ejemplo:
public static <T extends Comparable<? super T» T
max(Collection<T> coll)
Se borra a:
public static Comparable max(Collection coll)
La firma original era:
public static Object max(Collection coll)
Se arregla forzando a que la clase más general sea la especificada como primer
argumento en:
public static <T extends Object & Comparable<? super T» T
max(Collection<T> coll)
Verónica E. Arriola-Rios Actualizando Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Parchando
1 Tipos genéricos
2 Los genéricos ayudan únicamente en tiempo de compilación
3 Compatibilidad
4 Parchando
Verónica E. Arriola-Rios Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Creando arreglos de tipo T
Usar el método de la API Arrays:
1 public static <T > T [] copyOf ( T [] original , int newLength ) ;
Por ejemplo:
1 /* * Recibe como par á metro un arreglo de tama ñ o cero ,
2 el cual usa para el crear otro arreglo del tipo del
3 que se pas ó como par á metro . */
4 public static <T > T [] creaArregloT ( T [] crea , int length ) {
5 return Arrays . copyOf ( crea , length ) ;
6 }
7 ...
8 public static void main ( String [] args ) {
9 String [] arr = creaArreglo ( new String [0] , 10) ;
10 }
Verónica E. Arriola-Rios Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Referencias
Genéricos (en detalle)
[Link]
Genéricos (directo a lo avanzado)
[Link]
Verónica E. Arriola-Rios Facultad de Ciencias, UNAM
Tipos genéricos Sólo compilación Compatibilidad Parchando
Licencia
Creative Commons
Atribución-No Comercial-Compartir Igual
Verónica E. Arriola-Rios Facultad de Ciencias, UNAM