Должен ли я снова использовать примитивные типы?


16

Смешивание использования примитивных типов данных и их соответствующих классов-оболочек на Java может привести к множеству ошибок. Следующий пример иллюстрирует проблему:

int i = 4; 
... 
if (i == 10) 
    doStuff(); 

Позже вы поймете, что вы хотите, чтобы переменная я быть либо определено или не определено, так что вы измените выше экземпляра на:

Integer i = null; 

Теперь проверка равенства не выполняется.

Действительно ли практика Java всегда использует примитивные классы-оболочки? Очевидно, что некоторые ошибки будут устранены раньше, но каковы же недостатки? Это влияет на производительность или объем памяти приложения? Есть ли какие-нибудь подхалимы?

18

Использование типов в штучной упаковке У есть проблемы с производительностью и памятью.

При выполнении сравнений (например, (i == 10)) java должен распаковать тип перед выполнением сравнения. Даже с использованием i.equals(TEN) использует вызов метода, который является более дорогостоящим и (IMO) уродливым, чем синтаксис ==.

Re memory, объект должен храниться в куче (что также влияет на производительность), а также на сохранение самого значения.

Скрытый прием? i.equals(j) если есть null.

Я всегда использую примитивы, кроме случаев, когда это может быть null, но всегда проверять null перед сравнением в тех случаях.

  0

Он делает это другим способом, которым я верю (то есть: он помещает 10 в Целое число, затем сравнивает два экземпляра Integer, чтобы увидеть, являются ли они одним и тем же экземпляром, используя ==) 23 сен. 082008-09-23 22:58:46

+1

Нет. Он распаковывает Integer в примитивный int. 24 сен. 082008-09-24 00:22:50

+1

A * действительно * sneaky gotcha? i == j, когда i равно null. 24 сен. 082008-09-24 14:37:08


8

Во-первых, переход от использования примитива к использованию объекта только для того, чтобы получить возможность установить его в null, вероятно, является плохим дизайнерским решением. У меня часто бывают аргументы с моими коллегами о том, является ли это нулевым значением или нет, и мое мнение, как правило, заключается в том, что оно не является (и, следовательно, не должно быть запрещено, поскольку значения дозорного значения должны быть), но в этом конкретном случае вы собираетесь из вашего пути, чтобы использовать его в качестве контрольной суммы. Пожалуйста, не надо. Создайте логическое значение, указывающее, действительно ли ваше целое, или создайте новый тип, который объединяет логическое и целое число.

Обычно, при использовании более новых версий Java, мне не нужно явно создавать или отличать объектные версии примитивов из-за поддержки автоматического бокса, которая была добавлена ​​некоторое время в 1.5 (возможно, сама по себе 1.5) ,


0

Thee java POD типы есть по какой-то причине. Помимо накладных расходов, вы не можете выполнять обычные операции с объектами. Целое - это объект, который необходимо выделить и собрать мусор. Нет.

  0

Int может не быть объектом, но вам все же нужно выделить память для хранения, и эта память еще должна быть выпущена впоследствии. 23 сен. 082008-09-23 22:48:47

+1

@ Томми: Нет, нет. Он просто использует слот в стеке выполнения, который не считается «распределением». 23 сен. 082008-09-23 22:52:52

  0

+1 Крису и Дэвиду, -1 до Томми. Экземпляр Integer имеет все атрибуты объекта (потребляет намного больше, чем 4 байта, который использует примитивный int), и отслеживается независимо сборщиком мусора. Int находится только в стеке или в поле и умирает с его фреймом стека или обладающим экземпляром. 24 сен. 082008-09-24 00:37:35


1

В вашем примере, если заявление будет нормально, пока вы идете через 127 (как Integer Autoboxing будет кэшировать значения до 127 и возвращать тот же экземпляр для каждого номера до этого значения)

Так что хуже чем вы его представляете ...

if(i == 10) 

будет работать, как и раньше, но

if(i == 128) 

потерпит неудачу. Именно по таким причинам я всегда явно создаю объекты, когда они мне нужны, и, как правило, придерживаются примитивных переменных, если это вообще возможно

+4

Это неверно. Сравнение типа boxed с числовым примитивом приведет к распаковке типа boxed, и сравнение будет успешным, как ожидалось. Вы путаете реальную проблему, которая заключается в том, что сравнение двух типов в штучной упаковке неверно. I.e., учитывая «Integer v1 = 128, v2 = 128;» (v1 == v2) является ложным. 24 сен. 082008-09-24 00:33:18

+2

Вот хорошее объяснение кэширования Integer: http://www.owasp.org/index.php/Java_gotchas 24 сен. 082008-09-24 01:34:31


3

Я предлагаю использовать примитивы все время, если у вас действительно нет понятия «null» ».

Да, виртуальная машина делает autoboxing и все это сейчас, но это может привести к некоторым действительно странным случаям, когда вы получите исключение из null-указателя в строке кода, которую вы действительно не ожидаете, и вы должны начните выполнять нулевые проверки для каждой математической операции. Вы также можете начать получать некоторые неочевидные формы поведения, если вы начнете смешивать типы и получить более странное поведение с использованием autoboxing.

Для float/doubles вы можете рассматривать NaN как null, но помните, что NaN! = NaN, поэтому вам нужны специальные проверки, такие как Float.isNaN (x).

Было бы очень приятно, если бы были коллекции, которые поддерживали примитивные типы, вместо того, чтобы тратить время/накладные расходы на бокс.


0

Если это значение может быть пустым, вы можете обнаружить, что в вашем дизайне вам нужно что-то еще.

Существует две возможности: либо значение - это просто данные (код не будет действовать иначе, если он заполнен или нет), или это фактически указывает на то, что здесь есть два разных типа объекта (код действует иначе, если есть значение, отличное от нуля)

Если это просто данные для отображения/хранения, вы можете использовать реальный DTO - тот, у которого его нет как первоклассный член вообще. Обычно у них есть способ проверить, установлено ли значение или нет.

Если вы проверили нуль в какой-то момент, вы можете захотеть использовать подкласс, потому что, когда есть одна разница, обычно бывает больше. По крайней мере, вам нужен лучший способ указать вашу разницу, чем «если primitiveIntValue == null», это на самом деле ничего не значит.


0

Не переключайтесь на непримитивы, чтобы получить этот объект. Используйте boolean, чтобы указать, было ли задано значение или нет. Если вам не нравится это решение, и вы знаете, что ваши целые числа будут в некотором разумном пределе (или не заботятся о случайном сбое), используйте определенное значение, чтобы указать «неинициализированный», например Integer.MIN_VALUE. Но это гораздо менее безопасное решение, чем логическое.


0

Когда вы дошли до этого «Позже», перед рефакторингом необходимо выполнить еще немного работы. Используйте примитивы, когда это возможно. (Период капитала) Затем создайте POJO, если потребуется больше функциональности. На мой взгляд, примитивные классы-оболочки лучше всего использовать для данных, которые необходимо перемещать по проводу, что означает сетевые приложения. Разрешение нулей в качестве допустимых значений вызывает головную боль, поскольку система «растет». К большому количеству кода, потраченному впустую или пропущенному, следует соблюдать то, что должно быть простым сравнением.