Qué es y cómo funciona el type erasure en Java

Escrito por el .
java planeta-codigo programacion
Enlace permanente Comentarios

Los tipos genéricos en Java se implementaron usando type erasure por simplicidad en la implementación, no incurrir en penalizaciones de rendimiento o memoria y por mantener la compatibilidad con versiones anteriores de Java. Son varios los conceptos que están asociados a la implementación de los tipos genéricos en Java que es recomendable conocer como type erasure y métodos bridge de este artículo pero también heap pollution, non-reifiable, wildcards y bound type parameters.

Java

En la introducción de los tipos genéricos en Java con la versión 1.5 se decidió implementarlo usando type erasure que consiste en que en tiempo de ejecución se pierde la información de los tipos genéricos y para la máquina virtual no son distintos de un tipo no genérico, es un proceso que realiza el compilador. Esto tiene sus ventajas y algunas desventajas pero hay dos buenos motivos por los que en Java se decidió implementar los tipos genéricos usando type erasure.

Un motivo es que los tipos al ser en tiempo de ejecución exactamente iguales que los no genéricos de versiones anteriores se mantiene la compatibilidad hacia atrás tanto a nivel de código como a nivel binario lo que significa en un caso que el código fuente no es necesario que sea modificado y en otro que no es necesario recompilarlo y esto es importante para usar nuevas versiones de Java sin ningún tipo de modificación y para que programas antiguos sigan funcionando. El segundo motivo es que el mismo tipo sirve para todas las posibles instancias del tipo genérico, de forma que es eficiente y no se incurre en ninguna penalización de rendimiento o memoria.

La desventaja del type erasure es que en tiempo de ejecución no se pueden hacer algunas optimizaciones, en computación y uso de memoria. Sin embargo, evaluando las ventajas y desventajas los desarrolladores de Java siempre han dado gran importancia en la compatibilidad hacia atrás y por ello prefirieron implementar los generics usando type erasure.

El proceso de eliminar los tipos de los genéricos se realiza eliminando todos los parámetros de los tipos parametrizados siendo reemplazados con su restricción (bound), con el tipo Object o con con su restricción, si tiene múltiples restricciones se usa la primera.

En Java dos métodos distintos no pueden tener la misma firma, dado que los generics han sido implementados con type erasure también se ha de cumplir que dos métodos no pueden tener la misma firma una vez aplicado el erasure. Para no perder las validaciones de tipos el compilador inserta los cast necesarios. El código fuente de una clase genérica sería el siguiente, que el compilador transformaría siguiendo las reglas del type erasure.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class Node<T> {

    public T data;

    public Node(T data) { this.data = data; }

    public void setData(T data) {
        this.data = data;
    }
}

public class IntegerNode extends Node<Integer> {

    public IntegerNode(Integer data) { super(data); }
    
    public void setData(Integer data) {
        super.setData(data);
    }
}
Generic.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Node {

    public Object data;

    public Node(Object data) { this.data = data; }

    public void setData(Object data) {
        this.data = data;
    }
}

public class IntegerNode extends Node {

    public IntegerNode(Integer data) { super(data); }

    // Método bridge creado por el compilador
    // public void setData(Object data) {
    //    this.setData((Integer) data);
    // }

    public void setData(Integer data) {
        super.setData(data);
    }
}
Erased.java

Al mismo tiempo que el compilador inserta los cast necesarios para mantener la validación de tipos inserta métodos bridge para mantener el polimorfismo en las clases que extienden de tipos genéricos. Si se extiende la clase Node anterior y se aplica type erasure la firma del método setData de IntegerNode no coincide con el de la clase Node. Para solventar este problema el compilador inserta un método bridge para el método setData con un parámetro Object que se encarga de hacer de puente y llamar al método setData que recibe un Integer aplicando un cast.


Comparte el artículo: