Классы-обёртки
У каждого примитивного типа есть класс-обёртка — класс, объект которого хранит одно значение этого типа: int → Integer, double → Double, boolean → Boolean, char → Character (а также Long, Short, Byte, Float). Обёртки нужны там, где Java принимает только объекты: в коллекциях и в обобщённых типах.
Объект вместо числа
Метод Integer.valueOf() упаковывает число в объект, а intValue() достаёт его обратно:
void main() {
Integer box = Integer.valueOf(42);
int number = box.intValue();
IO.println(box);
IO.println(number + 1);
}Результат:
42
43У других обёрток методы называются так же: Double.valueOf() и doubleValue(), Boolean.valueOf() и booleanValue().
Автоупаковка и распаковка
Вызывать эти методы вручную почти никогда не нужно. Java сама упаковывает примитив в объект (автоупаковка) и распаковывает объект обратно (распаковка), когда типы не совпадают:
void main() {
Integer count = 5;
int next = count + 1;
List<Double> prices = new ArrayList<>();
prices.add(9.99);
double first = prices.get(0);
IO.println(next);
IO.println(first);
}Результат:
6
9.99В строке count + 1 объект распаковался в int, а в prices.add(9.99) число упаковалось в Double. Списки и словари подробно разобраны в главе о коллекциях — здесь достаточно знать, что они хранят только объекты.
Полезные методы
Обёртки — ещё и наборы готовых методов для своего типа. Integer.parseInt() уже знаком по уроку о преобразовании типов, а вот другие:
void main() {
IO.println(Integer.MAX_VALUE);
IO.println(Integer.compare(3, 7));
IO.println(Character.isDigit('7'));
IO.println(Character.isLetter('ж'));
IO.println(Character.toUpperCase('ж'));
IO.println(Boolean.parseBoolean("true"));
}Результат:
2147483647
-1
true
true
Ж
trueInteger.compare(a, b) возвращает отрицательное число, ноль или положительное — в зависимости от того, какое число больше.
null вместо значения
Переменная примитивного типа всегда хранит число, а переменная-обёртка может хранить null — «значения нет». Это удобно, когда отсутствие значения отличается от нуля:
void main() {
Integer bonus = null;
IO.println("Бонус: " + bonus);
}Результат:
Бонус: nullСравнение через ==
Оператор == для объектов проверяет, один ли это объект, а не равны ли значения. Java заранее создаёт объекты Integer для чисел от −128 до 127 и переиспользует их — это кеш Integer. Поэтому для маленьких чисел == случайно работает, а для больших — нет:
void main() {
Integer a = 127;
Integer b = 127;
IO.println(a == b);
Integer c = 128;
Integer d = 128;
IO.println(c == d);
IO.println(c.equals(d));
}Результат:
true
false
trueЗначения обёрток сравнивайте методом equals() или распакуйте их в int.
Частая ошибка
Если распаковать null, программа упадёт с NullPointerException. Чаще всего это случается со словарём: get() для отсутствующего ключа возвращает null, а переменная int не может его принять:
void main() {
Map<String, Integer> stock = Map.of("яблоки", 10);
int pears = stock.get("груши");
IO.println(pears);
}Результат:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.get(Object)" is nullИспользуйте stock.getOrDefault("груши", 0) или сохраняйте результат в переменную Integer и проверяйте её на null.