¿Por qué Java Generics no admite tipos primitivos?
¿Por qué los genéricos en Java funcionan con clases pero no con tipos primitivos?
Por ejemplo, esto funciona bien:
List<Integer> foo = new ArrayList<Integer>();
pero esto no está permitido:
List<int> bar = new ArrayList<int>();
Los genéricos en Java son una construcción enteramente en tiempo de compilación: el compilador convierte todos los usos genéricos en conversiones al tipo correcto. Esto es para mantener la compatibilidad con versiones anteriores de tiempos de ejecución de JVM.
Este:
List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);
se convierte en (aproximadamente):
List list = new ArrayList();
list.add(new ClassA());
ClassA a = (ClassA)list.get(0);
Entonces, cualquier cosa que se use como genérico debe poder convertirse en Objeto (en este ejemplo get(0)
devuelve un Object
), y los tipos primitivos no lo son. Por lo que no se pueden utilizar en genéricos.
En Java, los genéricos funcionan como lo hacen... al menos en parte... porque se agregaron al lenguaje varios años después de que se diseñara el lenguaje 1 . Los diseñadores de lenguajes se vieron limitados en sus opciones para los genéricos al tener que idear un diseño que fuera compatible con el lenguaje existente y la biblioteca de clases Java .
Otros lenguajes de programación (por ejemplo, C++, C#, Ada) permiten el uso de tipos primitivos como tipos de parámetros para genéricos. Pero la otra cara de la moneda es que las implementaciones de genéricos (o tipos de plantillas) en dichos lenguajes normalmente implican la generación de una copia distinta del tipo genérico para cada parametrización de tipo.
1 - La razón por la que los genéricos no se incluyeron en Java 1.0 fue por presión de tiempo. Sintieron que tenían que lanzar rápidamente el lenguaje Java para aprovechar la nueva oportunidad de mercado que presentaban los navegadores web. James Gosling ha declarado que le hubiera gustado incluir genéricos si hubieran tenido tiempo. Cómo habría sido el lenguaje Java si esto hubiera sucedido es una incógnita.
En Java, los genéricos se implementan mediante el uso de "borrado de tipo" para compatibilidad con versiones anteriores. Todos los tipos genéricos se convierten en Objetos en tiempo de ejecución. Por ejemplo,
public class Container<T> {
private T data;
public T getData() {
return data;
}
}
se verá en tiempo de ejecución como,
public class Container {
private Object data;
public Object getData() {
return data;
}
}
El compilador es responsable de proporcionar una conversión adecuada para garantizar la seguridad de tipos.
Container<Integer> val = new Container<Integer>();
Integer data = val.getData()
se convertirá
Container val = new Container();
Integer data = (Integer) val.getData()
Ahora la pregunta es ¿por qué se elige "Objeto" como tipo en tiempo de ejecución?
La respuesta es Objeto es una superclase de todos los objetos y puede representar cualquier objeto definido por el usuario.
Dado que no todas las primitivas heredan de " Objeto ", no podemos usarlo como un tipo genérico.
Para su información: el Proyecto Valhalla está intentando solucionar el problema anterior.
Según la documentación de Java , las variables de tipo genérico solo se pueden crear instancias con tipos de referencia, no con tipos primitivos.
Se supone que esto vendrá en Java 10 bajo el Proyecto Valhalla .
En el artículo de Brian Goetz sobre el estado de la especialización
Hay una excelente explicación sobre el motivo por el cual los genéricos no eran compatibles con los primitivos. Y cómo se implementará en futuras versiones de Java.
La implementación actual borrada de Java que produce una clase para todas las instancias de referencia y no admite instancias primitivas. (Esta es una traducción homogénea, y la restricción de que los genéricos de Java solo pueden abarcar tipos de referencia proviene de las limitaciones de la traducción homogénea con respecto al conjunto de códigos de bytes de la JVM, que utiliza diferentes códigos de bytes para operaciones en tipos de referencia frente a tipos primitivos). Sin embargo, los genéricos borrados en Java proporcionan tanto parametricidad de comportamiento (métodos genéricos) como parametricidad de datos (instanciaciones sin formato y comodines de tipos genéricos).
...
Se eligió una estrategia de traducción homogénea, donde las variables de tipo genérico se borran hasta sus límites a medida que se incorporan al código de bytes. Esto significa que, ya sea que una clase sea genérica o no, aún se compila en una sola clase, con el mismo nombre y cuyas firmas de miembros son las mismas. La seguridad de tipos se verifica en tiempo de compilación y el tiempo de ejecución no está limitado por el sistema de tipos genérico. A su vez, esto impuso la restricción de que los genéricos solo podían funcionar con tipos de referencia, ya que Objeto es el tipo más general disponible y no se extiende a tipos primitivos.