Классы-обёртки

Определение

Для каждого примитивного типа есть ссылочная обёртка:

ПримитивОбёртка
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

Void соответствует ключевому слову void только как служебный ссылочный тип.

Обёртки нужны там, где API работает с объектами: в generics, коллекциях, nullable-контрактах, reflection. Они неизменяемы.

List<Integer> ids = List.of(10, 20, 30);
Integer nullableCount = null;

Boxing и unboxing

Компилятор умеет автоматически оборачивать и извлекать примитив:

Integer boxed = 42; // Integer.valueOf(42)
int value = boxed;  // boxed.intValue()

Unboxing null приводит к NullPointerException:

Integer count = null;
// int next = count + 1; // NPE до сложения

В горячих циклах и больших коллекциях boxing создаёт лишние объекты/ссылки и может заметно увеличить память. Поэтому Stream API имеет IntStream, LongStream и DoubleStream.

Кэш и сравнение

valueOf может возвращать кэшированный объект. Для Integer гарантирован кэш как минимум диапазона -128..127, поэтому == иногда случайно выглядит рабочим:

Integer a = 100;
Integer b = 100;
System.out.println(a == b);      // true в гарантированном диапазоне
 
Integer x = 1000;
Integer y = 1000;
System.out.println(x == y);      // не следует ожидать true
System.out.println(x.equals(y)); // true

== для ссылок сравнивает идентичность, equals — значение. Для nullable-значений безопасен Objects.equals(a, b).

Парсинг и преобразование

int primitive = Integer.parseInt("42");
Integer object = Integer.valueOf("42");
String text = Integer.toString(42);
int hex = Integer.parseInt("ff", 16);

Некорректная строка вызывает NumberFormatException. Integer.decode также распознаёт некоторые префиксы системы счисления, но для внешнего контракта лучше явно задавать ожидаемый формат.

Сравнение чисел

Для сортировки используйте Integer.compare, Long.compare, Double.compare, а не вычитание:

Comparator<Job> byPriority =
        (left, right) -> Integer.compare(left.priority(), right.priority());

Вычитание может переполниться. Для Double дополнительно важны NaN, бесконечности и различие 0.0/-0.0; контракт Double.compare задаёт устойчивый порядок.

Ловушки

  • Unboxing nullable-значения.
  • Сравнение обёрток через ==.
  • Лишний boxing в производительном коде.
  • Использование обёртки как способа не продумывать отсутствие значения.
  • Парсинг без обработки NumberFormatException.

Вопросы на собеседовании

  1. Зачем нужны классы-обёртки?
  2. Что такое boxing и unboxing?
  3. Почему Integer == Integer иногда возвращает true, а иногда false?
  4. Что произойдёт при unboxing null?
  5. Чем parseInt отличается от valueOf?

Связанные заметки

Переменные и типы данных Java · Преобразование типов Java · Работа с числовыми данными · Класс Optional · Обобщения generics