Почему автобокс в Java позволяет мне иметь 3 возможных значения для логического значения?
Ссылка: http://java.sun.com/j2se/1.5.0/docs/guide/language/autoboxing.html
"Если ваша программа попытается автоматически установить null, она выдаст исключение NullPointerException".
javac выдаст вам ошибку времени компиляции, если вы попытаетесь присвоить null логическому значению. имеет смысл. присваивать null логическому значению - это нормально. также имеет смысл, я думаю.
но давайте подумаем о том, что вы получите NPE, когда попытаетесь автоматически установить NULL. это означает, что вы не можете безопасно выполнять логические операции над логическими значениями без проверки нуля или обработки исключений. То же самое касается выполнения математических операций над целым числом.
В течение долгого времени я был фанатом автобокса в java1.5+, потому что я думал, что он стал ближе к Java, чтобы быть действительно объектно-ориентированным. но, столкнувшись с этой проблемой прошлой ночью, я должен сказать, что я думаю, что это отстой. хорошо, что компилятор выдает ошибку, когда я пытаюсь сделать что-то с неинициализированным примитивом. Я не хочу использовать автобокс, если я потеряю это.
Я думаю, что, возможно, неправильно понимаю смысл автобокса, но в то же время я никогда не приму, что логическое значение должно иметь 3 значения. кто-нибудь может объяснить это? что я не получаю?
9 ответов
Типы в штучной упаковке являются ссылочными типами, и все ссылочные типы, примитивные блоки или нет, могут ссылаться на null
, Вот почему Boolean
может относиться к null
, Так может Integer
, Так может String
, так далее.
Типы в штучной упаковке не предназначены для того, чтобы сделать Java действительно объектно-ориентированной. Java никогда не будет чисто объектно-ориентированным языком, и вам не следует кодировать так, как будто это так. Примитивные типы никогда не исчезнут, и фактически должны быть предпочтительнее, когда есть выбор.
Вот цитата из Effective Java 2nd Edition, Item 49: Предпочитайте примитивные типы коробочным примитивам (выделено автором):
Таким образом, используйте примитивы в предпочтении по сравнению с примитивом в штучной упаковке, когда у вас есть выбор Примитивные типы проще и быстрее. Если вы должны использовать коробочные примитивы, будьте осторожны! Автобокс уменьшает многословность, но не опасность использования коробочных примитивов. Когда ваша программа сравнивает два коробочных примитива с
==
оператор, он делает сравнение идентичности, что почти наверняка не то, что вы хотите. Когда ваша программа выполняет вычисления смешанного типа с использованием коробочных и распакованных примитивов, она выполняет распаковку, а когда ваша программа выполняет распаковку, она может выдатьNullPointerException
, Наконец, когда ваша программа упаковывает примитивные значения, это может привести к дорогостоящим и ненужным созданиям объектов.
Я видел по крайней мере один случай, когда null
значение полезно. Многие объекты данных в веб-сервисах имеют логические поля, допускающие обнуляемое значение. Иногда пользователь не учитывает значение. В этом случае вы хотите, чтобы можно было отличить отсутствие значения от значения по умолчанию. Раньше люди писали getX
, setX
, а также isXSet()
методы, где isXSet
возвращает false, пока кто-то не позвонит setX
, Теперь можно сделать X
быть обнуляемым типом, и ясно, что он не был установлен, если getX
возвращается null
,
В дополнение ко всему сказанному здесь, есть случаи, когда вы очень хотели бы иметь третье значение для логических значений - случай "необязательного" свойства.
Мы обычно сталкиваемся с базами данных, с логическими столбцами, которые допускают нулевые значения. Без использования логических значений нам потребуется использовать две отдельные переменные, одна из которых указывает значение, а другая - допустимо ли оно.
На самом деле, если вы посмотрите на JDBC API, вы увидите пример этой проблемы, где столбцы получают значение по умолчанию, если они равны нулю (например, 0 для числовых полей), а затем вам нужно вызвать "wasNull" для проверьте, является ли это истинным 0 или фальшивым нулем!
Ваша проблема с автоматическими боксом, а не автобоксом. Я думаю, что автоматическая коробка зла по более важным причинам:
Учти это:
Integer i = new Integer(1);
Integer i2 = new Integer(12);
System.out.println(i == 10 || i != i2);
Один ==
распаковывает, а другой нет.
Распаковка операторов (в отличие от присваиваний), на мой взгляд, была ошибкой (учитывая вышеизложенное - это просто не Java). Бокс, однако, очень хорош.
Я думаю, что это скорее философский, чем технический вопрос. Когда вы преобразуете примитивный тип в ссылочный тип, вы должны быть готовы к тому, что ссылочные типы (т.е. объекты) обнуляются.
Вы можете посмотреть презентацию The Billion Dollars Mistake, где CAR Hoare говорит, что его введение нулевых ссылок на oop (Алгол 60) было ошибкой.
Джош Блох в Effective Java рекомендует предпочитать примитивные типы там, где это возможно. Но иногда вам приходится проверять булеву переменную на нуль.
С введением автобокса никогда не предполагалось, что он заменит примитивы. В большинстве мест примитивы - это то, что вы хотите. Если вы хотите начать использовать ссылочные типы, потому что "он стал ближе к Java, чтобы быть по-настоящему объектно-ориентированным", тогда это ваш вызов, но, как вы обнаружили, у этого подхода есть недостатки. Производительность будет другой проблемой.
Autoboxing (и autounboxing) существует для того, чтобы помочь с переходами между кодом, который использует примитивы (большинство), и кодом, который должен использовать ссылочные типы (редко). Из ссылочных типов, Boolean, несомненно, является самым редким, поскольку его небольшое количество экземпляров означает, что его почти никогда не стоит помещать в коллекцию.
В итоге: если ссылочные типы вызывают проблемы, не используйте их. Но не говорите, что "автобокс плох" только потому, что они не помогли вам в вашей ситуации.
Автобокс автоматически преобразует типы данных между внутренними типами и типами объектов.
Типы объектов для логического типа могут быть Boolean.TRUE
, Boolean.FALSE
, или же null
, Вот почему вы должны иметь дело с возможными 3 значениями для логического значения.
В базах данных обычно существует три состояния для логического типа. Рассмотрим запись, которая отслеживает, прошел ли кто-то класс. Есть ИСТИНА, для прохождения; ЛОЖЬ для не прохождения, и НЕДЕЙСТВИТЕЛЬНЫЙ для "в настоящее время взятие класса". Да, это странно, но отсутствие значения наследуется в объектно-ориентированном программировании.
Я также нахожу автобокс немного неприятным, в основном потому, что это особенность, когда компилятор добавляет байт-код для управления процессом преобразования. На мой взгляд, это может привести к тому, что люди забудут о важных деталях процесса конвертации, которые лучше запомнить (например, об обработке нуля в вашем случае). Это полезно, но не так полезно, как большинство других функций языка Java.
Лично я хотел бы, чтобы встроенные функции были реализованы как легкие объекты вместо "встроенных" типов. Во многих случаях мешает гибридная система внутренних / объектных типов. Тем не менее, встроенные функции должны были присутствовать для повышения производительности, но, похоже, что если вам необходимо многое сделать для маршаллинга объектов, вы не сможете насладиться только повышением производительности.
На самом деле нет большой разницы со днями до Java 1.5 - проблема не в булевом типе (он все еще имеет два состояния), а в булевой оболочке (которая всегда имела 3 состояния: Boolean.TRUE, Boolean.FALSE и null).
Каждое преобразование из логического объекта в логический примитив требует нулевых проверок с автобоксом или без него.
Это проблема с автобоксом, так же, как Integer i = null;
, Integer
объект может быть нулевым, в то время как родной int
не может быть.