2010-04-08 6 views
11

Autoboxing довольно страшно. В то время как я полностью понять разницу между == и .equals я не могу не помочь иметь последующую ошибку чёрта из меня:Почему компилятор/JVM просто не делает autoboxing «просто работать»?

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

Это печатает

true 
false 

Почему они делают это таким образом? Это как-то связано с кешированными целыми, но если это так, почему бы им не кэшировать все целые числа, используемые программой? Или почему JVM всегда автоматически распаковывается в примитив?

Печать ложных или истинных истин была бы лучше.

EDIT

Я не согласен о поломке старого кода. Имея foo.get(0) == bar.get(0) return true, вы уже нарушили код.

Не может это быть решена на уровне компилятора, заменив Integer с междунар в байт-код (до тех пор, как он никогда не назначается нуль)

+8

это как раз работа !! не так, как вы ожидали;) – OscarRyz

+0

На самом деле ваш пример мало связан с автобоксингом, это поведение предшествует ему. Это правда, что autoboxing заставляет нас больше знать об этом: equals() является (обычно правильным) способом сравнения объектов, == является (единственным) способом сравнения примитивов ... autoboxing помогает программисту обрабатывать Integer и int (почти) взаимозаменяемо ... и, следовательно, опасность ошибок здесь. – leonbloy

+0

Это может быть связано с особенностями, которые также влияют на экземпляры String. Насколько я понимаю, в зависимости от ценности происходит некоторая группировка за кулисами.Это можно предотвратить, используя ключевое слово 'new'. –

ответ

9
  • Почему они делают это таким образом?

Каждое целое число от -128 до 127 кэшируется java. Они сделали это, предположительно, для повышения производительности. Даже если они захотят вернуться к этому решению сейчас, вряд ли они это сделают. Если кто-то создал код в зависимости от этого, их код сломался бы, когда он был вынут. Для кодирования хобби это, возможно, не имеет значения, но для корпоративного кода люди расстраиваются, и судебные процессы случаются.

  • Почему они не кэшируют все целые числа, используемые программой?

Все целые числа нельзя кэшировать, потому что последствия памяти будут огромными.

  • Почему JVM всегда автоматически распаковывается в примитив?

Поскольку JVM не может знать, чего вы хотели. Кроме того, это изменение может легко разбить устаревший код, не созданный для обработки этого случая.

Если JVM автоматически распаковывается в примитивы при вызовах ==, эта проблема фактически станет БОЛЕЕ запутанной. Теперь вам нужно помнить, что == всегда сравнивает ссылки на объекты, если объекты не могут быть распакованы. Это вызовет еще более странные запутывающие случаи, как тот, который вы указали выше.

Скорее тогда беспокоиться слишком трудно об этом, просто помните это правило вместо:

НИКОГДА сравнивать объекты с ==, если вы не собираетесь быть сравнивая их по ссылкам. Если вы это сделаете, я не могу придумать сценарий, в котором вы столкнулись бы с проблемой.

+0

Никогда не говорите ** Никогда **. Вы ** должны ** сравнивать значения 'enum' с ==. –

+0

@ Александр Погребняк - Вы правы, и именно поэтому я добавил, что «если вы не собираетесь сравнивать их по их ссылкам». Это охватывает перечисления. Я стою на своем ** никогда ** :) –

+2

Справедливая точка. Тогда у вас все еще есть ваша «Лицензия на убийство -9» :). 007 out –

7

Можете ли вы представить себе, как плохой производительности будет, если каждый Integer осуществляется накладным интернирования? Также не работает для new Integer.

Язык Java (не проблема JVM) не всегда может автоматически распаковываться, потому что код, предназначенный для pre-1.5 Java, должен по-прежнему работать.

+0

хорошее упоминание о коде pre-1.5. +1 – Bozho

+0

еще один +1 для упоминания проблемы обратной совместимости – Chris

+0

они уже сломали старый код путем кэширования -128 до 127 – Pyrolistical

5

Integer s в байтовом диапазоне - это тот же объект, потому что они кэшированы. Integer s вне диапазона байтов нет. Если все целые числа должны быть кэшированы, представьте, что требуется память.

И от here

Результат всей этой магии является то, что вы можете в значительной степени игнорировать различия между междунар и Integer, с некоторыми оговорками. Выражение Integer может иметь нулевое значение. Если ваша программа пытается autounbox null, она выкинет исключение NullPointerException. Оператор == выполняет сравнения ссылочной идентичности по выражениям Integer и сравнениям значений равенства по выражениям int. Наконец, существуют издержки производительности, связанные с боксом и распаковка, даже если это делается автоматически

+0

'Целое слово' 'вне [-128, 127] может быть или не быть интернированным. (И я думаю, что исходный вопрос понимает, что происходит, но хочет знать почему?) –

+0

Я думаю, что они не с реализацией солнца. Во всяком случае, речь идет о кэшировании и ресурсах, необходимых для кэширования. – Bozho

4

Если вы полностью откажетесь от автобоксинга, вы все равно получите это поведение.

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 

быть более явным, если вы хотите определенное поведение:

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 

Это является причиной, почему Затмение Autoboxing как предупреждение по умолчанию.

+0

Это не включено по умолчанию в моей копии Eclipse, и я не изменил его. Какую версию ты используешь? Я проверил 3.2 и 3.4. – Chris

+0

@Crhis: Eclipse Gallileo (3.5) Window-> Настройки Java-> Компилятор-> Ошибки/Предупреждения-> Потенциальные проблемы программирования-> Бокс и Unboxing Conversions. Может быть, он по умолчанию не включен, но у меня это было с тех пор, как я перешел на Java 5. –

+0

По умолчанию он не включен. Я просто изменил его, когда прочитал об этом здесь. –

3

У многих людей проблемы с этой проблемой, даже люди, которые пишут книги о Java.

В Pro Java Programming, всего лишь несколько дюймов ниже, автор рассказывал о проблемах с использованием автоматических коробочных целых чисел в качестве ключа в IdentityHashMap, он использует автоматические коробочные ключи Integer в файле WeakHashMap. Используемые им значения превышают 128, поэтому его вызов коллекции мусора завершается успешно. Если кто-то должен был использовать свой пример и использовать значения, меньшие 128, то его пример потерпит неудачу (из-за того, что ключ был перма-кэширован).

2

Когда вы пишете

foo.get(0) 

компилятор не имеет значения, как вы создали список. Он смотрит только на тип компиляции List foo. Итак, если это список < Integer>, он будет рассматривать это как список < Integer>, как это и должно быть сделано, и список < Целое>> get() всегда возвращает Integer. Если вы хотите использовать ==, то вы должны написать

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

не

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

потому, что имеет совершенно иной смысл.

Смежные вопросы