Перейти к содержимому
Теория · Обёртки и равенство · урок 1 из 8

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

Чему вы научитесь

Чему вы научитесь:

  • называть обёртку каждого примитива и превращать строку в число через Integer.parseInt;
  • находить места, где компилятор сам упаковывает и распаковывает число, и объяснять, почему распаковка null останавливает программу;
  • объяснять, почему == для двух Integer даёт true на 127 и false на 128, и сравнивать обёртки через equals;
  • переопределять equals в своём классе, чтобы объекты сравнивались по полям.

Представьте монету и конверт. Монета — само значение: её держат в руке, и кроме номинала у неё ничего нет. Конверт — бумажный пакет с надписью, внутри которого лежит одна монета. Монету кладут в конверт, когда её нужно передать туда, где принимают только конверты. А ещё конверта может не быть совсем, и это тоже ответ: «монеты пока нет».

В Java примитив int — монета: в переменной лежит само число, как в коробке из темы о переменных. Обёртка Integer — конверт: объект в куче, внутри которого лежит одно число. В переменной типа Integer хранится ссылка на этот объект.

Но класс Integer нужен не только для конвертов. Начнём с задачи, которая встречается постоянно. Числа часто приходят в программу текстом: из аргументов args, из файла, из поля ввода на сайте. А текст "42" — это два символа, а не число:

JAVA
public class Main {
    public static void main(String[] args) {
        String text = "42";
        System.out.println(text + 1); // 421
        int n = Integer.parseInt(text);
        System.out.println(n + 1);    // 43
    }
}

Давайте разберём по строкам:

  1. В text лежат символы 4 и 2, а не число сорок два.
  2. text + 1 приклеивает к строке единицу и печатает 421.
  3. Integer.parseInt(text) читает цифры строки и возвращает число 42 типа int.
  4. n + 1 складывает два числа и печатает 43.

Integer здесь — не переменная, а класс, и parseInt — его static-метод. Вы уже встречали этот класс: константа Integer.MAX_VALUE, самое большое значение int, живёт в нём же. Это класс-обёртка для int.

Восемь обёрток

Класс-обёртка — класс, объект которого хранит одно значение примитивного типа. У каждого из восьми примитивов своя обёртка:

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

Почти везде имя обёртки — имя примитива с заглавной буквы. Запомнить нужно два исключения: int → Integer и char → Character.

Обёртка — это объект

Положить монету в конверт можно методом Integer.valueOf, а достать её — методом intValue:

JAVA
Integer boxed = Integer.valueOf(42);
int back = boxed.intValue();
System.out.println(back + 1); // 43
Integer score = null;
System.out.println(score);    // null

Разберём по строкам:

  1. Integer.valueOf(42) возвращает объект Integer с числом 42 внутри, и в boxed попадает ссылка на него.
  2. boxed.intValue() достаёт число из объекта: в back лежит обычный int.
  3. back + 1 печатает 43.
  4. Integer score = null; — переменная обёртки может хранить null: конверта нет.
  5. System.out.println(score) печатает слово null.

Зачем нужен null? Представьте оценку за экзамен, который ещё не сдан. В int score лежал бы 0, но ноль — тоже настоящая оценка, и эти случаи не различить. Integer score = null честно говорит: оценки пока нет.

Примитив так не умеет. Строка int n = null; не компилируется с ошибкой incompatible types: <null> cannot be converted to int: в переменной int всегда лежит какое-то число.

Здесь картинка с конвертом неточна. Настоящий конверт можно вскрыть и подменить монету. Объект Integer запечатан навсегда: число в нём не меняется, как символы в строке. Чтобы получить другое число, берут другой объект.

Полезные методы обёрток

В классах-обёртках собраны инструменты для своего типа. Их вызывают через имя класса, как Integer.parseInt, без всякого объекта:

JAVA
double price = Double.parseDouble("2.5");
System.out.println(price * 2);         // 5.0
long big = Long.parseLong("3000000000");
System.out.println(big + 1);           // 3000000001
Integer seven = Integer.valueOf("7");
System.out.println(seven);             // 7

Из строки в число

У каждой числовой обёртки есть свой parse-метод: Integer.parseInt, Long.parseLong, Double.parseDouble. Строка для parseInt должна целиком быть целым числом: цифры и, если нужно, знак - или + в начале. Пробел, буква или точка — и метод остановит программу при запуске. Число должно ещё и поместиться в int: строку "3000000000" из примера читают через Long.parseLong.

valueOf или parseInt

Integer.valueOf тоже принимает строку: Integer.valueOf("7"). Разница в том, что эти методы возвращают:

  • Integer.parseInt — примитив int;
  • Integer.valueOf — объект Integer.

Константы

Границы целых типов лежат в константах их обёрток: Integer.MIN_VALUE и Integer.MAX_VALUE, Long.MIN_VALUE и Long.MAX_VALUE.

Зачем нужны обёртки

На собеседовании часто спрашивают: зачем в Java обёртки, если есть примитивы? Хороший ответ состоит из трёх частей:

  • обёртка делает из числа объект, а многие части стандартной библиотеки, например коллекции, хранят только объекты;
  • переменная обёртки может хранить null, то есть «значения нет»;
  • в классе-обёртке живут инструменты для её типа: parseInt, valueOf, MAX_VALUE.

Частая ошибка

Цену читают из текста методом parseInt:

JAVA
String priceText = "3.5";
int price = Integer.parseInt(priceText); // ✗
TEXT
Exception in thread "main" java.lang.NumberFormatException: For input string: "3.5"

Программа компилируется: компилятор не заглядывает внутрь строк. Ошибку находит parseInt при запуске: целое число не записывают с точкой. Метод бросает исключение NumberFormatException и называет строку, которую не смог прочитать. Дробное число читают через Double.parseDouble, а строку с пробелами по краям сначала очищают методом strip().

Главное

  • У каждого примитива есть класс-обёртка: int → Integer, char → Character, у остальных — то же имя с заглавной буквы.
  • Обёртка — неизменяемый объект с одним значением; переменная обёртки хранит ссылку и может хранить null.
  • Integer.parseInt превращает строку в int, а Integer.valueOf — в объект Integer.
  • Строка, которая не целиком число, например "3.5", даёт при запуске NumberFormatException.