Вітання! Ти вже непогано знайомий із примітивними типами, і чимало з ними попрацював.
У примітивів у програмуванні, і Java зокрема, є безліч переваг: вони займають мало пам'яті, за рахунок чого підвищується ефективність роботи програми, і чітко розділені по діапазонах значень. Однак у процесі вивчення Java ми неодноразово, немов мантру, повторювали — “
в Java все є об'єктом ”. Адже примітиви — пряме спростування цих слів. Об'єктами вони є. Виходить, принцип "все є об'єктом" є хибним? Насправді ні. У Java у кожного примітивного типу є свій брат-близнюк -
клас-обгортка (
Wrapper
). Що таке обгортка?
Обгортка - це спеціальний клас, який зберігає в собі значення примітиву. Але оскільки це саме клас, він може створювати свої екземпляри. Вони зберігатимуть усередині потрібні значення примітивів, у своїй будуть справжніми об'єктами. Назви класів-оберток дуже схожі на назви відповідних примітивів, або повністю з ними збігаються. Тож запам'ятати їх буде дуже легко.
Wrapper Classes for Primitive Data Types |
Primitive Data Types |
Wrapper Classes |
int |
Integer |
short |
Short |
long |
Long |
byte |
Byte |
float |
Float |
double |
Double |
char |
Character |
boolean |
Boolean |
Об'єкти класів обгорток створюються так само, як і будь-які інші:
public static void main(String[] args) {
Integer i = new Integer(682);
Double d = new Double(2.33);
Boolean b = new Boolean(false);
}
Класи-обертки дозволяють нівелювати недоліки, які є у примітивних типів. Найочевидніший із них —
примітиви не мають методів . Наприклад, у них немає методу
toString()
, тому ти не зможеш, наприклад, перетворити число
int
на рядок. А ось із класом-обгорткою
Integer
— запитто.
public static void main(String[] args) {
Integer i = new Integer(432);
String s = i.toString();
}
Виникнуть складнощі і зі зворотним перетворенням. Припустимо, у нас є рядок, про який ми точно знаємо, що він містить число. Тим не менш, у випадку з примітивним типом
int
ми ніяк не зможемо це число з рядка дістати і перетворити, власне, на число. Але завдяки класам-обгорткам така можливість у нас з'явилася.
public static void main(String[] args) {
String s = "1166628";
Integer i = Integer.parseInt(s);
System.out.println(i);
}
Висновок: 1166628 Ми успішно отримали число з рядка і привласнабо його в змінну-посилання
Integer i
. До речі, щодо посилань. Ти вже знаєш, що параметри передаються в методи по-різному: примітиви за значенням, а об'єкти за посиланням. Ти можеш використовувати це знання при створенні своїх методів: якщо твій метод працює, наприклад, з дробовими числами, але тобі потрібна логіка саме передачі за посиланням, ти можеш передати метод параметри
Double/Float
замість
double/float
. Крім того, крім методів у класах-обгортках є дуже зручні для використання статичні поля. Наприклад, уяви, що перед тобою зараз стоїть завдання:
вивести в консоль максимально можливе число int
, а потім мінімально можливе. Завдання начебто елементарне, а все одно — без гугла навряд чи впораєшся. А класи-обгортки легко дозволяють вирішувати такі “побутові завдання”:
public class Main {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
}
}
Такі поля дозволяють не відволікатися від більш серйозних завдань. Не кажучи вже про те, що в процесі друку числа
2147483647 (це якраз MAX_VALUE) не дивно і опечататися:) Крім того, в одній з минулих лекцій ми вже звертали увагу на те, що об'єкти класів-
обгорток є незмінними (Immutable) .
public static void main(String[] args) {
Integer a = new Integer(0);
Integer b = new Integer(0);
b = a;
a = 1;
System.out.println(b);
}
Висновок: 0 Об'єкт, на який спочатку вказувало посилання
а
, не змінив свій стан, інакше значення
b
теж змінилося б. Як і у випадку зі
String
замість зміни стану об'єкта-обгортки в пам'яті створюється абсолютно новий об'єкт. Чому ж творці Java, зрештою, вирішабо залишити у мові примітивні типи? Якщо все має бути об'єктом, і ми вже маємо класи-обгортки, якими можна висловити все, що виражають примітиви, чому взагалі не залишити в мові тільки їх, а примітиви видалити? Відповідь проста - продуктивність. Примітивні типи тому і називають примітивними, тому що вони позбавлені багатьох "важкоатлетних" особливостей об'єктів. Так, об'єкт має багато зручних методів, але ж вони не завжди тобі потрібні. Іноді тобі потрібно просто число 33, чи 2,62, чи значення
true
/
false
. У ситуаціях, коли всі переваги об'єктів не мають значення і не потрібні для роботи програми, примітиви впораються із завданням набагато краще.
Автоупаковка/автораспаковка
Однією з особливостей примітивів та їх класів-обгорток у Java є автоупаковка/автораспаковка (Autoboxing/Autounboxing)
Давай розберемося з цим поняттям. Як ми з тобою вже дізналися раніше, Java - об'єктно-орієнтована мова. Це означає, що всі програми, написані Java, складаються з об'єктів. Примітиви є об'єктами. Але при цьому змінної класу-обгортки можна надавати значення примітивного типу. Цей процес називається
автоупаковкою (
autoboxing ). Так само змінної примітивного типу можна надавати об'єкт класу-обгортки.
Цей процес називається авторапакуванням (autounboxing) . Наприклад:
public class Main {
public static void main(String[] args) {
int x = 7;
Integer y = 111;
x = y;
y = x * 123;
}
}
У рядку 5 ми присвоюємо примітиву x значення y, який є об'єктом класу-обгортки
Integer
. Як бачиш, ніяких додаткових дій для цього не потрібно:
компілятор знає що int
і Integer
, по суті, те саме . Це і є авторопакування. Також відбувається і автоупаковка в рядку 6: об'єкту y легко присвоюється значення примітивів (х*123). Це приклад автоупаковки. Саме тому додається слово "авто":
для присвоєння посилань-примітивів об'єктам їх класів-оберток (і навпаки) не потрібно нічого робити, все відбувається автоматично . Зручно, правда? :) Ще одна дуже велика зручність автоупаковки/автораспаковки проявляється в роботі методів. Справа в тому, що
параметри методів теж підлягають автоупаковці та автоупаковці. І, наприклад, якщо один із них приймає на вхід два об'єкти
Integer
— ми легко можемо передати туди звичайні примітиви
int
!
public class Main {
public static void main(String[] args) {
printNumber(7);
}
public static void printNumber(Integer i) {
System.out.println("Ви ввели число" + i);
}
}
Висновок: Ви ввели число 7 Так само працює і навпаки:
public class Main {
public static void main(String[] args) {
printNumber(new Integer(632));
}
public static void printNumber(int i) {
System.out.println("Ви ввели число" + i);
}
}
Важливий момент, про який потрібно пам'ятати:
автоупаковка та розпакування не працюють для масивів !
public class Main {
public static void main(String[] args) {
int[] i = {1,2,3,4,5};
printArray(i);
}
public static void printArray(Integer[] arr) {
System.out.println(Arrays.toString(arr));
}
}
Спроба передати масив примітивів метод, який приймає на вхід масив об'єктів, викличе помилку компіляції. Насамкінець, ще раз коротко порівняємо примітиви та обгортки
.
- мають перевагу у продуктивності
Обгортки:
- Дозволяють не порушувати принцип “все є об'єктом”, завдяки чому цифри, символи та булеві значення true/false не випадають із цієї концепції
- Розширюють можливості роботи з цими значеннями, надаючи зручні методи та поля
- Необхідно, коли якийсь метод може працювати виключно з об'єктами
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ