Avec Java 9, de nouvelles méthodes d'usine ont été introduites pour les interfaces List
, Set
et Map
. Ces méthodes permettent d'instancier rapidement une carte objet avec des valeurs sur une ligne. Maintenant, si nous considérons:
Map<Integer, String> map1 = new HashMap<Integer, String>(Map.of(1, "value1", 2, "value2", 3, "value3"));
map1.put(4, null);
Ce qui précède est autorisé sans exception alors que si nous le faisons:
Map<Integer, String> map2 = Map.of(1, "value1", 2, "value2", 3, "value3", 4, null );
Il jette:
Exception in thread "main" Java.lang.NullPointerException
at Java.base/Java.util.Objects.requireNonNull(Objects.Java:221)
..
Je ne parviens pas à obtenir pourquoi la valeur null n'est pas autorisée dans le deuxième cas.
Je sais que HashMap peut prendre null comme clé ainsi que comme valeur, mais pourquoi cela a-t-il été limité dans le cas de Map.of?
La même chose se produit dans le cas de Java.util.Set.of("v1", "v2", null)
et Java.util.List.of("v1", "v2", null)
.
Comme d'autres l'ont fait remarquer, le Map
contrat permet de rejeter les valeurs nulles ...
[Quelques] implémentations interdisent
null
clés et valeurs [...]. Tenter d’insérer une clé ou une valeur non éligible lève une exception non contrôlée, généralementNullPointerException
ouClassCastException
.
... et les usines de collecte (pas seulement sur les cartes) tilisez-le .
Ils interdisent
null
clés et valeurs. Les tentatives pour les créer avec les clés ou les valeursnull
donnentNullPointerException
.
Autoriser null
dans les collections est maintenant considéré comme une erreur de conception. Cela a une variété de raisons. Un bon exemple est la convivialité, où le fauteur de troubles le plus important est Map::get
. S'il renvoie null
, il est difficile de savoir si la clé est manquante ou si la valeur était null
. De manière générale, les collections garanties null
free sont plus faciles à utiliser. Du point de vue de la mise en œuvre, ils requièrent également des boîtiers moins spéciaux, ce qui rend le code plus facile à gérer et plus performant.
Vous pouvez écouter Stuart Marks l'expliquer dans cet exposé mais JEP 269 (celui qui a introduit les méthodes d'usine) le résume également:
Les éléments nuls, les clés et les valeurs ne seront pas autorisés. (Aucune collection récemment introduite ne prend en charge les valeurs NULL.) En outre, l'interdiction des valeurs NULL offre des possibilités de représentation interne plus compacte, d'accès plus rapide et de moins de cas spéciaux.
Puisque HashMap
était déjà dans la nature lorsque cela a été découvert lentement, il était trop tard pour le changer sans rompre le code existant mais les implémentations les plus récentes de ces interfaces (par exemple, ConcurrentHashMap
) n'autorise plus null
et les nouvelles collections pour les méthodes d'usine ne font pas exception.
(Je pensais qu'une autre raison était que l'utilisation explicite de null
valeurs était considérée comme une erreur d'implémentation probable, mais je me suis trompé. Cela impliquait de dupliquer des clés, qui sont également illégales.)
Donc, le refus de null
avait une raison technique, mais cela a également été fait pour améliorer la robustesse du code en utilisant les collections créées.
Exactement - un HashMap
est autorisé à stocker null, pas le Map
renvoyé par les méthodes de fabrique statique. Pas tous les cartes sont les mêmes.
En règle générale, autant que je sache, les erreurs les plus récentes dans la clé HashMap
constituent une erreur, mais les nouvelles collections interdisent cette possibilité.
Pensez au cas où vous avez une entrée dans votre HashMap
qui a une certaine clé et une certaine valeur == null. Vous obtenez, il retourne null
. Qu'est-ce que cela signifie? Il a un mappage de null
ou il n'est pas présent?
Il en va de même pour un Key
- un hashcode provenant d'une telle clé nulle doit être traité de manière spécifique tout le temps. Interdire les valeurs NULL pour commencer - facilite les choses.
Bien que HashMap autorise les valeurs NULL, Map.of
n'utilise pas HashMap
et lève une exception si elle est utilisée comme clé ou comme valeur, comme documenté :
Les méthodes d'usine statiques Map.of () et Map.ofEntries () constituent un moyen pratique de créer des cartes immuables. Les instances de carte créées par ces méthodes présentent les caractéristiques suivantes:
...
Ils interdisent les clés et les valeurs nulles. Les tentatives pour les créer avec des clés ou des valeurs NULL entraînent NullPointerException.
Autoriser les valeurs NULL dans les cartes a été une erreur. Nous pouvons le voir maintenant , mais je suppose que ce n'était pas clair quand HashMap
a été introduit. NullpointerException
est le bogue le plus courant observé dans le code de production.
Je pourrais dire que le JDK va dans le sens d'aider les développeurs à lutter contre le fléau des NPE. Quelques exemples:
Optional
Collectors.toMap(keyMapper, valueMapper)
n'autorise ni la fonction keyMapper
ni la fonction valueMapper
à renvoyer une valeur null
Stream.findFirst()
et Stream.findAny()
jettent NPE si la valeur trouvée est null
Donc, refuser null
dans les nouvelles collections immuables JDK9 (et la carte) va dans la même direction. Et nous devrions tous en être remerciés!
La principale différence est la suivante: lorsque vous créez votre propre carte, la méthode "option 1" ... vous êtes implicitement en disant: "Je veux avoir liberté dans ce que je fais ".
Ainsi, lorsque vous décidez que votre carte doit avoir une clé ou une valeur nulle (peut-être pour les raisons énumérées ici ), vous êtes libre de le faire.
Mais "option 2" est une question de commodité - probablement destinée à être utilisée pour les constantes. Et les personnes derrière Java ont simplement décidé: "lorsque vous utilisez ces méthodes pratiques, la carte résultante est alors sans valeur nulle".
Permettre des valeurs nulles signifie que
if (map.contains(key))
n'est pas la même chose que
if (map.get(key) != null)
ce qui peut être un problème parfois. Ou plus précisément: il faut se souvenir de cet objet cartographique.
Et juste une indication anecdotique de la raison pour laquelle cela semble être une approche raisonnable: notre équipe a elle-même mis en œuvre des méthodes de commodité similaires. Et devinez quoi: sans rien savoir des projets d’avenir Java - nos méthodes procèdent exactement de la même manière: elles renvoient une copie immuable des données entrantes; Nous sommes même si stricts que lorsque vous passez des listes vides /cartes/... nous nous plaignons également.
Toutes les cartes n'autorisent pas la nullité en tant que clé
La raison mentionnée dans le docs of Map
.
Certaines implémentations de carte ont des restrictions sur les clés et les valeurs qu'elles peuvent contenir. Par exemple, certaines implémentations interdisent les clés et les valeurs nulles, et certaines ont des restrictions sur les types de leurs clés. Tenter d'insérer une clé ou une valeur non éligible lève une exception non contrôlée, généralement NullPointerException ou ClassCastException.
La documentation ne dit pas pourquoinull
n'est pas autorisé:
Ils interdisent
null
clés et valeurs. Les tentatives pour les créer avec les clés ou les valeursnull
aboutissent àNullPointerException
.
À mon avis, les méthodes fabriques statiques Map.of()
et Map.ofEntries()
, qui vont produire une constante, sont principalement formées par un développeur du type compilé. Alors, quelle est la raison de garder un null
comme clé ou valeur?
Tandis que le Map#put
est généralement utilisé pour remplir une carte au moment de l'exécution où null
clés/valeurs peuvent apparaître.