Pourquoi Java Generics ne prend-il pas en charge les types primitifs?

236

Pourquoi les génériques en Java fonctionnent-ils avec les classes mais pas avec les types primitifs?

Par exemple, cela fonctionne bien:

List<Integer> foo = new ArrayList<Integer>();

mais ce n'est pas autorisé:

List<int> bar = new ArrayList<int>();
sgokhales
la source
1
int i = (int) new Object (); compile très bien cependant.
Sachin Verma

Réponses:

243

Les génériques en Java sont une construction entièrement au moment de la compilation - le compilateur transforme toutes les utilisations génériques en transtypages au bon type. Il s'agit de maintenir la compatibilité descendante avec les runtimes JVM précédents.

Ce:

List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);

devient (grossièrement):

List list = new ArrayList();
list.add(new ClassA());
ClassA a = (ClassA)list.get(0);

Donc, tout ce qui est utilisé comme générique doit être convertible en Object (dans cet exemple, get(0)renvoie un Object), et les types primitifs ne le sont pas. Ils ne peuvent donc pas être utilisés dans les génériques.

thecoop
la source
8
@DanyalAytekin - En fait, les génériques Java ne sont PAS du tout traités comme des modèles C ++ ...
Stephen C
20
Pourquoi le compilateur Java ne peut-il pas également encadrer le type primitif avant qu'il ne soit utilisé? Cela devrait être possible non?
vrwim
13
@vrwim - Cela pourrait être possible. Mais ce ne serait que du sucre syntaxique. Le vrai problème est que les génériques Java avec des primitives encadrées sont relativement coûteux en temps et en espace par rapport au modèle C ++ / C # ... où le type de primitive réel est utilisé.
Stephen C
6
@MauganRa ouais je sais que je peux :) Je maintiens mon point de vue que c'est terrible tho de conception. J'espère que cela sera corrigé dans java 10 (du moins, j'ai entendu) et dans des fonctions d'ordre supérieur. Ne me citez pas là-dessus.
Ced
4
@Ced est entièrement d'accord sur le fait que c'est une mauvaise conception qui blesse sans cesse les débutants comme les professionnels
MauganRa
37

En Java, les génériques fonctionnent comme ils le font ... au moins en partie ... car ils ont été ajoutés au langage un certain nombre d'années après la conception du langage 1 . Les concepteurs de langage ont été contraints dans leurs options pour les génériques en devant proposer une conception qui était rétrocompatible avec le langage existant et la bibliothèque de classes Java .

D'autres langages de programmation (par exemple C ++, C #, Ada) permettent aux types primitifs d'être utilisés comme types de paramètres pour les génériques. Mais le revers de la médaille est que les implémentations génériques (ou types de modèles) de ces langages impliquent généralement la génération d'une copie distincte du type générique pour chaque paramétrage de type.


1 - La raison pour laquelle les génériques n'étaient pas inclus dans Java 1.0 était à cause de la pression du temps. Ils ont estimé qu'ils devaient faire sortir le langage Java rapidement pour saisir la nouvelle opportunité de marché présentée par les navigateurs Web. James Gosling a déclaré qu'il aurait aimé inclure des génériques s'ils en avaient eu le temps. À quoi le langage Java aurait-il ressemblé si cela s'était produit?

Stephen C
la source
11

En java, les génériques sont implémentés en utilisant "Effacement de type" pour une compatibilité descendante. Tous les types génériques sont convertis en objet au moment de l'exécution. par exemple,

public class Container<T> {

    private T data;

    public T getData() {
        return data;
    }
}

sera vu à l'exécution comme,

public class Container {

    private Object data;

    public Object getData() {
        return data;
    }
}

le compilateur est responsable de fournir une distribution appropriée pour assurer la sécurité du type.

Container<Integer> val = new Container<Integer>();
Integer data = val.getData()

va devenir

Container val = new Container();
Integer data = (Integer) val.getData()

Maintenant, la question est de savoir pourquoi "Object" est choisi comme type lors de l'exécution?

La réponse est Objet est la superclasse de tous les objets et peut représenter n'importe quel objet défini par l'utilisateur.

Puisque toutes les primitives n'héritent pas de " Object ", nous ne pouvons donc pas l'utiliser comme type générique.

Pour info: le projet Valhalla tente de résoudre le problème ci-dessus.

Piyush Sagar
la source
Plus 1 pour une nomenclature appropriée.
Drazen Bjelovuk
7

Les collections sont définies pour exiger un type qui dérive de java.lang.Object. Les types de base ne font tout simplement pas cela.

ZeissS
la source
26
Je pense que la question ici est "pourquoi". Pourquoi les génériques nécessitent-ils des objets? Le consensus semble être qu'il s'agit moins d'un choix de conception et plus de céder à la compatibilité descendante. À mes yeux, si les génériques ne peuvent pas gérer les primitives, c'est un déficit de fonctionnalité. Dans l'état actuel des choses, tout ce qui implique des primitives doit être écrit pour chaque primitive: au lieu de Comparateur <t, t>, nous avons Integer.compare (int a, int b), Byte.compare (octet a, octet b), etc. Ce n'est pas une solution!
John P
1
Ouais les génériques sur les types primitifs seraient une fonctionnalité incontournable. Voici un lien vers une proposition pour cela openjdk.java.net/jeps/218
crow
5

Selon la documentation Java , les variables de type générique ne peuvent être instanciées qu'avec des types de référence, pas des types primitifs.

Cela est censé venir en Java 10 dans le cadre du projet Valhalla .

Dans un article de Brian Goetz sur l' état de la spécialisation

Il existe une excellente explication de la raison pour laquelle les génériques n'étaient pas pris en charge pour les primitifs. Et comment il sera implémenté dans les futures versions de Java.

L'implémentation effacée actuelle de Java qui produit une classe pour toutes les instanciations de référence et aucun support pour les instanciations primitives. (Il s'agit d'une traduction homogène, et la restriction selon laquelle les génériques Java ne peuvent s'étendre qu'aux types de référence provient des limitations de la traduction homogène en ce qui concerne l'ensemble de bytecodes de la JVM, qui utilise différents bytecodes pour les opérations sur les types de référence par rapport aux types primitifs.) Cependant, les génériques effacés en Java fournissent à la fois la paramétricité comportementale (méthodes génériques) et la paramétricité des données (instanciations brutes et génériques de types génériques).

...

une stratégie de traduction homogène a été choisie, où les variables de type générique sont effacées à leurs limites lorsqu'elles sont incorporées dans le bytecode. Cela signifie que, qu'une classe soit générique ou non, elle se compile toujours en une seule classe, avec le même nom et dont les signatures de membres sont les mêmes. La sécurité des types est vérifiée au moment de la compilation et l'exécution n'est pas entravée par le système de type générique. À son tour, cela a imposé la restriction selon laquelle les génériques ne pouvaient fonctionner que sur les types de référence, car Object est le type le plus général disponible et ne s'étend pas aux types primitifs.

vins
la source
0

Lors de la création d'un objet, vous ne pouvez pas substituer un type primitif au paramètre type. Quant au pourquoi de cette restriction, c'est un problème d'implémentation du compilateur. Les types primitifs ont leurs propres instructions de bytecode pour le chargement et le stockage sur la pile de machines virtuelles. Il n'est donc pas impossible de compiler des génériques primitifs dans ces chemins de bytecode séparés, mais cela compliquerait le compilateur.

Atif
la source