¿Por qué Java Generics no admite tipos primitivos?

236

¿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>();
Sgokhales
fuente
1
int i = (int) nuevo Object (); compila muy bien sin embargo.
Sachin Verma

Respuestas:

243

Los genéricos en Java son una construcción completamente 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 JVM.

Esta:

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);

Por lo tanto, cualquier cosa que se use como genéricos debe ser convertible a Object (en este ejemplo get(0)devuelve un Object), y los tipos primitivos no lo son. Por lo tanto, no se pueden usar en genéricos.

thecoop
fuente
8
@DanyalAytekin - De hecho, los genéricos de Java NO se manejan como plantillas de C ++ ...
Stephen C
20
¿Por qué el compilador Java no puede también encasillar el tipo primitivo antes de que se use? Esto debería ser posible ¿verdad?
vrwim
13
@vrwim: podría ser posible. Pero solo sería azúcar sintáctico. El verdadero problema es que los genéricos de Java con primitivas en caja son relativamente caros en tiempo y espacio en comparación con el modelo C ++ / C # ... donde se usa el tipo de primitiva real.
Stephen C
66
@MauganRa, sí, sé que puedo :) Mantengo mi argumento de que este es un diseño terrible. Esperemos que se arregle en Java 10 (o eso escuché) y también en funciones de orden superior. No me cites por eso.
Ced
44
@Ced está totalmente de acuerdo en que es un mal diseño que perjudica sin fin a los principiantes y a los profesionales por igual
MauganRa
37

En Java, los genéricos funcionan de la manera en que lo hacen ... al menos en parte ... porque se agregaron al lenguaje varios años después de que el lenguaje fue diseñado 1 . Los diseñadores de idiomas se vieron limitados en sus opciones de 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 que se usen tipos primitivos como tipos de parámetros para genéricos. Pero la otra cara de hacer esto es que las implementaciones de genéricos (o tipos de plantillas) de tales lenguajes generalmente implican la generación de una copia distinta del tipo genérico para cada parametrización de tipo.


1 - La razón por la cual los genéricos no se incluyeron en Java 1.0 se debió a la presión del tiempo. Sintieron que tenían que obtener el lenguaje Java lanzado rápidamente para llenar la nueva oportunidad de mercado presentada por los navegadores web. James Gosling ha declarado que le hubiera gustado incluir genéricos si hubieran tenido tiempo. Lo que habría parecido el lenguaje Java si esto hubiera sucedido es una incógnita.

Stephen C
fuente
11

En java, los genéricos se implementan mediante el uso de "Borrado de tipo" para la 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;
    }
}

será visto en tiempo de ejecución como,

public class Container {

    private Object data;

    public Object getData() {
        return data;
    }
}

El compilador es responsable de proporcionar un molde adecuado para garantizar la seguridad del tipo.

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é "Objeto" se elige como tipo en tiempo de ejecución?

La respuesta es Object es la superclase de todos los objetos y puede representar cualquier objeto definido por el usuario.

Como todas las primitivas no heredan del " Objeto ", no podemos usarlo como un tipo genérico.

FYI: Proyecto Valhalla está tratando de abordar el problema anterior.

Piyush Sagar
fuente
Más 1 para la nomenclatura adecuada.
Drazen Bjelovuk
7

Las colecciones se definen para requerir un tipo derivado de java.lang.Object. Los tipos básicos simplemente no hacen eso.

ZeissS
fuente
26
Creo que la pregunta aquí es "por qué". ¿Por qué los genéricos requieren objetos? El consenso parece ser que es menos una opción de diseño y más ceder a la compatibilidad con versiones anteriores. En mi opinión, si los genéricos no pueden manejar las primitivas, es un déficit de funcionalidad. Tal como está, todo lo que involucra primitivas debe escribirse para cada primitiva: en lugar de Comparator <t, t>, tenemos Integer.compare (int a, int b), Byte.compare (byte a, byte b), etc. ¡Esa no es una solución!
John P
1
Sí, los genéricos sobre los tipos primitivos serían una característica imprescindible. Aquí hay un enlace a una propuesta abiertajdk.java.net/jeps/218
crow
5

Según la documentación de Java , las variables de tipo genérico solo se pueden instanciar con tipos de referencia, no con tipos primitivos.

Se supone que esto vendrá en Java 10 bajo el Proyecto Valhalla .

En el documento de Brian Goetz sobre el estado de la especialización

Hay una excelente explicación sobre la razón por la cual los genéricos no eran compatibles con los primitivos. Y cómo se implementará en futuras versiones de Java.

La implementación borrada actual de Java que produce una clase para todas las instancias de referencia y no es compatible con las instancias primitivas. (Esta es una traducción homogénea, y la restricción de que los genéricos de Java solo pueden variar sobre 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 la parametricidad del comportamiento (métodos genéricos) como la parametricidad de los datos (instancias 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 si una clase es 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 tipo se verifica en tiempo de compilación, y el sistema de tipo genérico no limita el tiempo de ejecución. A su vez, esto impuso la restricción de que los genéricos solo podían funcionar sobre los tipos de referencia, ya que Object es el tipo más general disponible, y no se extiende a los tipos primitivos.

vinS
fuente
0

Al crear un objeto, no puede sustituir un tipo primitivo por el parámetro de tipo. En cuanto al por qué de esta restricción, es un problema de implementación del compilador. Los tipos primitivos tienen sus propias instrucciones de código de bytes para cargar y almacenar en la pila de máquinas virtuales. Por lo tanto, no es imposible compilar genéricos primitivos en estas rutas de bytecode separadas, pero complicaría el compilador.

Atif
fuente