1. Вступ
У мові Java існує два сімейства даних. Перше — це примітивні типи: int, char, double, boolean та інші. Вони прості й швидкі: у них значення зберігається безпосередньо в комірці памʼяті. Друге сімейство — це посилальні типи, тобто обʼєкти: змінна містить посилання на місце в памʼяті, де розташовано обʼєкт.
Іноді потрібно використати примітив як обʼєкт, проте примітиви від початку не є обʼєктами. У таких випадках допомагають спеціальні класи-обгортки.
Типи-обгортки — це класи, що всередині тримають значення примітиву та дають змогу працювати з ним як із обʼєктом. Наприклад, для int — клас Integer, для double — Double, для char — Character, для boolean — Boolean. Такі класи надають методи, зокрема: Integer.parseInt(), Double.isInfinite(), Character.isLetter(), Boolean.parseBoolean() тощо.
Приклад отримання обʼєкта Integer з примітива int:
// примітив
int a = 10;
// обʼєкт-обгортка
Integer b = Integer.valueOf(10);
У змінній a зберігається саме число 10, а в змінній b — посилання на обʼєкт, що всередині містить 10.
2. Навіщо потрібні обгортки
Примітивне значення не може дорівнювати null. Натомість посилання на обʼєкт може бути порожнім — null. Це зручно, коли треба позначити «невідомо» або «немає».
У примітивів немає методів. Не можна викликати для числа, наприклад, x.toString(). Обгортка дає такі можливості: Integer.parseInt("123") перетворює рядок на число, а число можна перетворити на рядок за допомогою toString().
Висновок: обгортки потрібні там, де потрібен саме обʼєкт — можливість зберігати null, викликати методи, передавати значення в API, що приймає лише обʼєкти (наприклад, колекції на кшталт List<Integer>).
Повний список типів-обгорток
| Примітивний тип | Клас-обгортка |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3. Пакування та розпакування
Коли ви вручну створюєте обʼєкт-обгортку, берете примітив і «пакуєте» його всередину обʼєкта:
int primitive = 42;
Integer wrapper = Integer.valueOf(primitive); // пакування
Щоб знову отримати примітивне значення — «розпакувати» — використовують, наприклад, intValue():
Integer wrapper = Integer.valueOf(42);
int primitive = wrapper.intValue(); // розпакування
Автобоксинг (Autoboxing)
Компілятор автоматично додасть виклик valueOf(), якщо ви присвоюєте примітив змінній-обгортці:
int a = 10;
Integer b = a; // автобоксинг (Integer.valueOf(a))
Анбоксинг (Unboxing)
Зворотна ситуація — коли обʼєкт-обгортка використовується там, де потрібен примітив:
Integer c = 20;
int d = c; // анбоксинг (c.intValue())
int a = 10; // примітив
Integer b = a; // автобоксинг
Integer c = Integer.valueOf(20);
int d = c; // анбоксинг
4. Підводні камені
Порівняння обгорток. Оператор == для обʼєктів порівнює посилання, а не значення:
Integer m = 200;
Integer n = 200;
System.out.println(m == n); // false, бо це різні обʼєкти
Java кешує обʼєкти-обгортки чисел у діапазоні від -128 до 127, тож інколи == поверне true, а інколи — false. Для порівняння значень завжди використовуйте equals():
Integer x = 100;
Integer y = 100;
System.out.println(x == y); // true, потрапили в кеш
System.out.println(x.equals(y)); // true, порівняння за значенням
NullPointerException під час анбоксингу. Якщо обʼєкт-обгортка дорівнює null, а ви намагаєтеся її розпакувати — отримаєте виняток:
Integer value = null;
int primitive = value; // Помилка! NullPointerException
Продуктивність і памʼять. Обгортки повільніші за примітиви й займають більше памʼяті. У «гарячих» ділянках коду та під час роботи з великими масивами чисел обирайте примітиви.
5. Приклади
Приклади роботи з Integer і Double
Перетворення рядка на число:
String text = "123";
int number = Integer.parseInt(text);
System.out.println(number); // 123
Перевірка спеціальних значень дійсних чисел:
double d = 1.0 / 0;
System.out.println(Double.isInfinite(d)); // true
double nan = 0.0 / 0.0;
System.out.println(Double.isNaN(nan)); // true
Приклади роботи з Character
char зберігає один символ; обгортка Character дає зручні перевірки:
char ch = 'A';
Character wrapper = ch; // автобоксинг
System.out.println(Character.isLetter(ch)); // true
System.out.println(Character.isDigit(ch)); // false
System.out.println(Character.toLowerCase(ch)); // 'a'
Приклади роботи з Boolean
Примітив boolean — це тільки true/false. Обгортка Boolean дозволяє зберігати ще й null:
Boolean flag = null; // припустимо
flag = Boolean.TRUE; // спеціальна константа
System.out.println(flag); // true
Перетворення рядків у логічні значення:
String s1 = "true";
String s2 = "false";
boolean b1 = Boolean.parseBoolean(s1);
boolean b2 = Boolean.parseBoolean(s2);
System.out.println(b1); // true
System.out.println(b2); // false
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ