Question

Autoboxing est assez effrayant. Même si je comprends bien la différence entre == et .equals je ne peux pas, mais aider à avoir le bug de suivi l'enfer hors de moi:

    final List<Integer> foo = Arrays.asList(1, 1000);
    final List<Integer> bar = Arrays.asList(1, 1000);
    System.out.println(foo.get(0) == bar.get(0));
    System.out.println(foo.get(1) == bar.get(1));

qui imprime

true
false

Pourquoi ont-ils de cette façon? Il quelque chose à voir avec Entiers mises en cache, mais si tel est le cas pourquoi ne pas mettre en cache tout simplement tous les Entiers utilisés par le programme? Ou pourquoi ne pas la machine virtuelle Java toujours de Unbox automatique à primitive?

Impression fausse vrai ou faux vrai aurait été bien meilleur.

EDIT

Je suis en désaccord au sujet de la rupture de l'ancien code. En ayant retour foo.get(0) == bar.get(0) vrai que vous avez déjà brisé le code.

peut pas être résolu au niveau du compilateur en remplaçant Entier avec int dans le code d'octet (pour autant qu'il ne soit jamais affecté null)

Était-ce utile?

La solution

  • Pourquoi ont-ils de cette façon?

Chaque entier entre -128 et 127 sont mises en cache par java. Ils l'ont fait, soi-disant, au profit de la performance. Même s'ils voulaient revenir sur cette décision maintenant, il est peu probable qu'ils le fassent. Si quelqu'un construit le code en fonction de cela, leur code briserait quand il a été retiré. Pour le codage passe-temps, cela ne peut-être pas d'importance, mais pour le code de l'entreprise, les gens se fâcher et procès se produire.

  • Pourquoi ne pas mettre en cache tout simplement tous les Entiers utilisés par le programme?

Tous les Entiers ne peuvent pas être mises en cache, car les implications de mémoire seraient énormes.

  • Pourquoi ne pas la machine virtuelle Java toujours de Unbox automatique à primitive?

Parce que la machine virtuelle Java ne peut pas savoir ce que vous vouliez. De plus, ce changement pourrait facilement se casser le code existant ne se construit pas pour gérer ce cas.

Si la machine virtuelle Java automatiquement Unboxed aux primitives sur les appels à ==, cette question sera effectivement devenir plus confus. Maintenant, vous devez vous rappeler que == compare toujours des références d'objet, à moins que les objets peuvent être Unboxed. Cela causerait des cas de confusion encore plus étranges comme celui que vous avez indiqué ci-dessus.

Plutôt que de vous inquiéter trop dur à ce sujet, rappelez-vous cette règle au lieu:

JAMAIS comparer avec des objets == à moins que vous avez l'intention d'être les comparer par leurs références. Si vous faites cela, je ne peux pas penser à un scénario dans lequel vous souhaitez exécuter dans un problème.

Autres conseils

Pouvez-vous imaginer à quel point la performance serait si chaque Integer porté frais généraux pour l'internement? Aussi ne fonctionne pas pour new Integer.

Le langage Java (JVM pas un problème) ne peut pas toujours Unbox automatique parce que le code conçu pour les pré-1.5 Java devrait toujours fonctionner.

Integers dans la plage d'octets sont le même objet, parce qu'ils sont mises en cache. Integers en dehors de la plage d'octets ne sont pas. Si tous les entiers devaient être mis en cache, imaginez la mémoire nécessaire.

Et de

  

Le résultat de toute cette magie est que vous pouvez largement ignorer la distinction entre int et entier, avec quelques mises en garde. Une expression entière peut avoir une valeur nulle. Si votre programme tente de autounbox null, il déclenchera une NullPointerException. L'opérateur == effectue des comparaisons de référence d'identité sur les expressions entières et des comparaisons d'égalité de valeur sur int expressions. Enfin, il y a des coûts de performance liés à la boxe et unboxing, même si elle se fait automatiquement

Si vous sautez autoboxing complètement, vous obtenez toujours ce comportement.

final List<Integer> foo =
  Arrays.asList(Integer.valueOf( 1 ), Integer.valueOf( 1000 ));
final List<Integer> bar =
  Arrays.asList(Integer.valueOf( 1 ), Integer.valueOf( 1000 ));

System.out.println(foo.get(0) == bar.get(0)); // true
System.out.println(foo.get(1) == bar.get(1)); // false

Soyez plus explicite si vous voulez un comportement spécifique:

final List<Integer> foo =
  Arrays.asList( new Integer( 1 ), new Integer( 1000 ));
final List<Integer> bar =
  Arrays.asList( new Integer( 1 ), new Integer( 1000 ));

System.out.println(foo.get(0) == bar.get(0)); // false
System.out.println(foo.get(1) == bar.get(1)); // false

Ceci est une raison, pourquoi Eclipse a autoboxing comme un avertissement par défaut.

Beaucoup de gens ont des problèmes avec cette question, même les gens qui écrivent des livres sur Java.

Dans Pro programmation Java , quelques pouces ci-dessous ont été les pourparlers de l'auteur au sujet des problèmes avec l'utilisation entiers auto-boxed comme une clé dans une IdentityHashMap, il utilise des clés entiers auto-boxed dans un WeakHashMap. Les exemples de valeurs qu'il utilise sont supérieurs à 128, de sorte que son appel de collecte des ordures réussit. Si quelqu'un devait utiliser son exemple et utiliser des valeurs plus petites que 128 cependant, son exemple échouerait (en raison de la clé étant perma-cache).

Lorsque vous écrivez

foo.get(0)

le compilateur n'a pas d'importance comment vous avez créé la liste. Il semble que le type de la liste foo compilation. Donc, si cela est une liste , il traitera que comme une liste , comme il est censé le faire, et une liste 's get () retourne toujours un entier. Si vous voulez utiliser le == alors vous devez écrire

System.out.println(foo.get(0).intValue() == bar.get(0).intValue());

pas

System.out.println(foo.get(0) == bar.get(0));

parce que cela a un sens tout à fait différent.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top