Должен ли я больше никогда не использовать примитивные типы?
Смешение использования примитивных типов данных и их соответствующих классов-оболочек в Java может привести к множеству ошибок. Следующий пример иллюстрирует проблему:
int i = 4;
...
if (i == 10)
doStuff();
Позже вы увидите, что вы хотите, чтобы переменная я была либо определена, либо undefined, поэтому вы изменяете описанную выше процедуру:
Integer i = null;
Теперь проверка равенства не выполняется.
Является ли хорошей практикой Java всегда использовать примитивные классы-оболочки? Очевидно, что некоторые ошибки будут устранены раньше, но каковы же недостатки? Это влияет на производительность или объем памяти приложения? Есть ли какие-нибудь скрытые gotchas?
Ответы
Ответ 1
Использование типов в коробке имеет проблемы с производительностью и памятью.
При выполнении сравнений (например, (i == 10)
) java должен отключить тип перед выполнением сравнения. Даже при использовании i.equals(TEN)
используется вызов метода, который является более дорогостоящим и (IMO) более уродливым, чем синтаксис ==.
Re memory, объект должен быть сохранен в куче (что также влияет на производительность), а также на сохранение самого значения.
Подкрадывается? i.equals(j)
, когда я null
.
Я всегда использую примитивы, кроме случаев, когда это может быть null
, но всегда проверяйте null
перед сравнением в этих случаях.
Ответ 2
Во-первых, переход от использования примитива к использованию объекта только для того, чтобы получить возможность установить его в null, вероятно, является плохим дизайнерским решением. У меня часто бывают аргументы с моими коллегами о том, является ли это нулевым значением или нет, и мое мнение, как правило, заключается в том, что оно не является (и, следовательно, не должно быть запрещено, поскольку значения дозорного значения должны быть), но в этом конкретном случае вы собираетесь из вашего пути, чтобы использовать его в качестве контрольной суммы. Пожалуйста, не надо. Создайте логическое значение, указывающее, действительно ли ваше целое, или создайте новый тип, который объединяет булево и целое число.
Обычно при использовании более новых версий Java я считаю, что мне не нужно явно создавать или применять к объектным версиям примитивов из-за поддержки автоматического бокса, которая была добавлена некоторое время в 1.5 (возможно, сама по себе 1.5).
Ответ 3
Я бы предложил использовать примитивы все время, если у вас действительно нет понятия "null".
Да, виртуальная машина делает autoboxing и все это сейчас, но это может привести к некоторым действительно более странным случаям, когда вы получите исключение с нулевым указателем в строке кода, которую вы действительно не ожидаете, и вы должны начать выполняя нулевые проверки для каждой математической операции. Вы также можете начать получать некоторые неочевидные поведения, если вы начнете смешивать типы и получить более странное поведение при автобоксинге.
Для float/doubles вы можете рассматривать NaN как null, но помните, что NaN!= NaN, поэтому вам все еще нужны специальные проверки: Float.isNaN(x).
Было бы очень приятно, если бы были коллекции, которые поддерживали примитивные типы, вместо того, чтобы тратить время/накладные расходы на бокс.
Ответ 4
В вашем примере оператор if будет работать до тех пор, пока вы не перейдете на 127 (поскольку Integer autoboxing будет кэшировать значения до 127 и возвращать один и тот же экземпляр для каждого номера до этого значения)
Так что это хуже, чем вы его представляете...
if( i == 10 )
будет работать по-прежнему, но
if( i == 128 )
не удастся. Именно по таким причинам я всегда явным образом создаю объекты, когда они мне нужны, и, как правило, всегда придерживаюсь примитивных переменных
Ответ 5
Те типы java POD существуют по какой-то причине. Помимо накладных расходов, вы не можете выполнять обычные операции с объектами. Целое - это объект, который необходимо выделить и собрать мусор. Нет int.
Ответ 6
Если это значение может быть пустым, вы можете обнаружить, что в вашем дизайне вам нужно что-то еще.
Есть две возможности: либо значение - это просто данные (код не будет действовать иначе, если он заполнен или нет), или это фактически указывает на то, что здесь есть два разных типа объекта (код действует по-разному если есть значение, отличное от нуля)
Если это просто данные для отображения/хранения, вы можете подумать о том, чтобы использовать реальный DTO - тот, у которого его нет как первоклассный член. Обычно у них есть способ проверить, установлено ли значение или нет.
Если вы проверите нуль в какой-то момент, вы можете захотеть использовать подкласс, потому что, когда есть одна разница, обычно бывает больше. По крайней мере, вам нужен лучший способ указать вашу разницу, чем "если primitiveIntValue == null", это ничего не значит.
Ответ 7
Не переключайтесь на не-примитивы, чтобы получить это средство. Используйте boolean, чтобы указать, было ли задано значение или нет. Если вам не нравится это решение, и вы знаете, что ваши целые числа будут в некотором разумном пределе (или не заботятся о случайном сбое), используйте определенное значение, чтобы указать "неинициализированный", например Integer.MIN_VALUE. Но это гораздо менее безопасное решение, чем логическое.
Ответ 8
Когда вы добрались до этого "Позже", в ходе рефакторинга нужно было выполнить еще немного работы. Используйте примитивы, когда это возможно. (Период капитала) Затем создайте POJO, если потребуется больше функциональности. На мой взгляд, примитивные классы-оболочки лучше всего использовать для данных, которые необходимо перемещать по проводу, что означает сетевые приложения. Разрешение нулей в качестве допустимых значений вызывает головную боль, поскольку система "растет". К большому количеству кода, потраченному впустую или пропущенному, охраняя то, что должно быть простым сравнением.