web-dev-qa-db-fra.com

Quel est le point de l'opérateur diamant (<>) dans Java 7?

L'opérateur losange dans Java 7 autorise le code suivant:

List<String> list = new LinkedList<>();

Cependant, dans Java 5/6, je peux simplement écrire:

List<String> list = new LinkedList();

Ma compréhension de l'effacement des types est que ceux-ci sont exactement les mêmes. (Le générique est quand même supprimé au moment de l'exécution).

Pourquoi s'embêter avec le diamant? Quelles nouvelles fonctionnalités/types de sécurité permettent-ils? Si cela ne donne aucune nouvelle fonctionnalité, pourquoi le mentionne-t-il comme une fonctionnalité? Est-ce que ma compréhension de ce concept est défectueuse?

434
tofarr

Le problème avec

List<String> list = new LinkedList();

est-ce que sur le côté gauche, vous utilisez le type génériqueList<String> alors que sur le côté droit vous utilisez le type brutLinkedList. Les types bruts dans Java n'existent effectivement que pour des raisons de compatibilité avec le code pré-génériques et ne doivent jamais être utilisés dans du nouveau code, sauf obligation absolue.

Maintenant, si Java avait des génériques depuis le début et n'avait pas de types, tels que LinkedList, qui avaient été créés à l'origine avant les génériques, il aurait probablement pu faire en sorte que le constructeur de un type générique déduit automatiquement ses paramètres de type du côté gauche de l'affectation, si possible. Mais ce n’est pas le cas, et il faut traiter les types bruts et les types génériques différemment pour assurer la compatibilité avec les versions antérieures. Cela leur laisse besoin de créer un moyen légèrement différent, mais tout aussi pratique, de déclarer une nouvelle instance d'un objet générique sans avoir à répéter ses paramètres de type ... l'opérateur de diamant.

En ce qui concerne votre exemple original de List<String> list = new LinkedList(), le compilateur génère un avertissement pour cette affectation, car il le doit. Considère ceci:

List<String> strings = ... // some list that contains some strings

// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);

Les génériques existent pour fournir une protection au moment de la compilation pour éviter les mauvaises actions. Dans l'exemple ci-dessus, l'utilisation du type brut signifie que vous ne bénéficiez pas de cette protection et qu'une erreur se produira lors de l'exécution. C'est pourquoi vous ne devez pas utiliser de types bruts.

// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);

Toutefois, l'opérateur losange permet de définir le côté droit de l'affectation comme une instance générique véritable avec les mêmes paramètres de type que le côté gauche ... sans avoir à saisir à nouveau ces paramètres. Il vous permet de conserver la sécurité des génériques avec presque le même effort que l’utilisation du type brut.

Je pense que l'élément clé à comprendre est que les types bruts (sans <>) ne peuvent pas être traités de la même manière que les types génériques. Lorsque vous déclarez un type brut, vous ne bénéficiez d'aucun des avantages et de la vérification de type des génériques. Vous devez également garder à l'esprit que les génériques font partie du langage Java à usage général ... ils ne s'appliquent pas uniquement aux constructeurs no-arg de Collections!

481
ColinD

Votre compréhension est légèrement imparfaite. L’opérateur de diamants est une fonctionnalité intéressante, car vous n’avez pas à vous répéter. Il est logique de définir le type une fois lorsque vous déclarez le type, mais cela n’a aucun sens de le redéfinir à droite. Le principe DRY.

Maintenant, expliquez tout le fuzz sur la définition des types. Vous avez raison de dire que le type est supprimé au moment de l'exécution, mais lorsque vous souhaitez extraire quelque chose d'une liste avec une définition de type, vous le récupérez sous le type que vous avez défini lors de la déclaration de la liste. Sinon, toutes les fonctionnalités spécifiques seraient perdues. Fonctionnalités d'objet, sauf lorsque vous convertissez l'objet extrait en son type d'origine, ce qui peut parfois s'avérer très délicat et donner lieu à une exception ClassCastException.

En utilisant List<String> list = new LinkedList() vous obtiendrez des avertissements de type brut.

34
Octavian Damiean

Cette ligne provoque l'avertissement [non coché]:

List<String> list = new LinkedList();

Ainsi, la question se transforme: pourquoi l'avertissement [non coché] n'est pas automatiquement supprimé uniquement dans le cas où une nouvelle collection est créée?

Je pense que ce serait une tâche beaucoup plus difficile que d'ajouter <> feature.

UPD: Je pense aussi qu'il y aurait un gâchis si c'était légalement d'utiliser des types bruts 'juste pour quelques petites choses'.

16
Roman

En théorie, l'opérateur losange vous permet d'écrire un code plus compact (et lisible) en sauvegardant des arguments de type répétés. En pratique, ce ne sont que deux caractères déroutants qui ne vous apportent rien. Pourquoi?

  1. Aucun programmeur sensé n'utilise des types bruts dans le nouveau code. Ainsi, le compilateur pourrait simplement supposer qu’en n’écrivant aucun argument de type, vous voulez qu’il les déduise.
  2. L’opérateur diamond ne fournit aucune information de type, il dit simplement au compilateur, "tout ira bien". Donc, en l'omettant, vous ne pouvez pas nuire. À tout endroit où l’opérateur de diamants est légal, cela peut être "déduit" par le compilateur.

IMHO, avoir un moyen simple et clair de marquer une source en tant que Java 7 serait plus utile que d'inventer de telles choses étranges. Dans un code aussi marqué, les types bruts pourraient être interdits sans rien perdre.

Btw., Je ne pense pas que cela devrait être fait en utilisant un commutateur de compilation. La version Java d'un fichier programme est un attribut du fichier, aucune option. Utiliser quelque chose d'aussi trivial que

package 7 com.example;

peut être clair (vous préférerez peut-être quelque chose de plus sophistiqué, notamment un ou plusieurs mots-clés sophistiqués). Cela permettrait même de compiler des sources écrites pour différentes versions de Java sans aucun problème. Cela permettrait d’introduire de nouveaux mots-clés (par exemple, "module") ou de supprimer certaines fonctionnalités obsolètes (plusieurs classes non publiques non imbriquées dans un seul fichier ou quoi que ce soit) sans perte de compatibilité.

13
maaartinus

Lorsque vous écrivez List<String> list = new LinkedList();, le compilateur génère un avertissement "non vérifié". Vous pouvez l'ignorer, mais si vous aviez l'habitude d'ignorer ces avertissements, vous risquez également de manquer un avertissement vous informant d'un problème de sécurité de type réel.

Il est donc préférable d’écrire un code qui ne génère pas d’avertissements supplémentaires, et l’opérateur Diamond vous permet de le faire de manière pratique sans répétition inutile.

8
axtavt

Tous les énoncés dans les autres réponses sont valables mais les cas d'utilisation ne sont pas complètement valides à mon humble avis. Si on vérifie Guava et en particulier les éléments liés aux collections, il en a été de même avec les méthodes statiques. Par exemple. Lists.newArrayList () qui vous permet d'écrire

List<String> names = Lists.newArrayList();

ou avec import statique

import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");

Goyave a d’autres fonctionnalités très puissantes comme celle-ci et je ne peux vraiment pas penser à de nombreuses utilisations du <>.

Il aurait été plus utile de choisir le comportement par défaut de l'opérateur diamant, c'est-à-dire que le type est déduit à partir du côté gauche de l'expression ou si le type du côté gauche était déduit à partir du côté droit. Ce dernier est ce qui se passe à Scala.

4
allprog

Le but de l’opérateur diamant est simplement de réduire le typage du code lors de la déclaration de types génériques. Cela n'a aucun effet sur le temps d'exécution.

La seule différence si vous spécifiez dans Java 5 et 6,

List<String> list = new ArrayList();

est que vous devez spécifier @SuppressWarnings("unchecked") sur list (sinon, vous obtiendrez un avertissement de diffusion non contrôlé). Je crois comprendre que l’opérateur de diamants tente de faciliter le développement. Il n'a absolument rien à faire lors de l'exécution des génériques à l'exécution.

3
Buhake Sindi