Классы-обёртки
Чему вы научитесь:
- называть обёртку каждого примитива и превращать строку в число через
Integer.parseInt; - находить места, где компилятор сам упаковывает и распаковывает число, и объяснять, почему распаковка
nullостанавливает программу; - объяснять, почему
==для двухIntegerдаётtrueна127иfalseна128, и сравнивать обёртки черезequals; - переопределять
equalsв своём классе, чтобы объекты сравнивались по полям.
Представьте монету и конверт. Монета — само значение: её держат в руке, и кроме номинала у неё ничего нет. Конверт — бумажный пакет с надписью, внутри которого лежит одна монета. Монету кладут в конверт, когда её нужно передать туда, где принимают только конверты. А ещё конверта может не быть совсем, и это тоже ответ: «монеты пока нет».
В Java примитив int — монета: в переменной лежит само число, как в коробке из темы о переменных. Обёртка Integer — конверт: объект в куче, внутри которого лежит одно число. В переменной типа Integer хранится ссылка на этот объект.
Но класс Integer нужен не только для конвертов. Начнём с задачи, которая встречается постоянно. Числа часто приходят в программу текстом: из аргументов args, из файла, из поля ввода на сайте. А текст "42" — это два символа, а не число:
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
}
}Давайте разберём по строкам:
- В
textлежат символы4и2, а не число сорок два. text + 1приклеивает к строке единицу и печатает421.Integer.parseInt(text)читает цифры строки и возвращает число42типаint.n + 1складывает два числа и печатает43.
Integer здесь — не переменная, а класс, и parseInt — его static-метод. Вы уже встречали этот класс: константа Integer.MAX_VALUE, самое большое значение int, живёт в нём же. Это класс-обёртка для int.
Восемь обёрток
Класс-обёртка — класс, объект которого хранит одно значение примитивного типа. У каждого из восьми примитивов своя обёртка:
| Примитив | Обёртка |
|---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
Почти везде имя обёртки — имя примитива с заглавной буквы. Запомнить нужно два исключения: int → Integer и char → Character.
Обёртка — это объект
Положить монету в конверт можно методом Integer.valueOf, а достать её — методом intValue:
Integer boxed = Integer.valueOf(42);
int back = boxed.intValue();
System.out.println(back + 1); // 43
Integer score = null;
System.out.println(score); // nullРазберём по строкам:
Integer.valueOf(42)возвращает объектIntegerс числом42внутри, и вboxedпопадает ссылка на него.boxed.intValue()достаёт число из объекта: вbackлежит обычныйint.back + 1печатает43.Integer score = null;— переменная обёртки может хранитьnull: конверта нет.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, без всякого объекта:
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:
String priceText = "3.5";
int price = Integer.parseInt(priceText); // ✗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.