1. Перетворення типів

Змінні примітивних типів (за винятком типу boolean) використовуються для зберігання чисел різних типів. І хоча типи змінних є незмінними, є спосіб, який дозволяє перетворювати типи. Цей спосіб — присвоювання.

Змінні різних типів можна присвоювати одна одній. У цьому разі значення, взяте зі змінної одного типу, буде перетворено на значення іншого типу й присвоєно другій змінній. Можна виділити два види перетворення типів: розширення і звуження.

Розширення типу схоже на перекладання з маленького кошика у великий — операція проходить непомітно і безболісно. Звуження типу — це перекладання з великого кошика в маленький: місця може не вистачити, і щось доведеться викинути.

Тут показано типи, відсортовані за розміром «кошика»:

Перетворення типів у Java 2


2. Розширення типів

Часто може з'явитися потреба присвоїти змінній одного числового типу значення змінної іншого числового типу. Як же це зробити?

У мові Java є 4 цілочислових типи:

Тип Розмір
byte 1 байт
short 2 байти
int 4 байти
long 8 байтів

Змінній більшого розміру завжди можна присвоювати змінні меншого розміру.

Змінній типу long можна без обмежень присвоювати змінні типу int, short і byte. Змінній типу int можна присвоювати змінні типу short і byte. А змінній типу short можна присвоювати змінні типу byte.

Приклади:

Код Опис
byte a = 5;
short b = a;
int c = a + b;
long d = c * c;
Такий код чудово скомпілюється.

Таке перетворення — від типу меншого розміру до більшого, називається розширенням типу.

А як це діє для дійсних чисел?

З ними все так само — розмір має значення:

Тип Розмір
float 4 байти
double 8 байтів

Змінній типу double можна без проблем присвоїти змінну типу float. А от з цілочисловими типами цікавіше.

Змінній типу float можна присвоїти змінну будь-якого цілочислового типу. Навіть типу long, довжина якого 8 байтів. А змінній типу double можна взагалі присвоювати все що завгодно: змінну будь-якого цілочислового типу й змінну типу float:

Код Примітка
long a = 1234567890;
float b = a;
double c = a;

b == 1.23456794E9
c == 1.23456789E9

Зверніть увагу, що перетворення на дійсний тип може призвести до втрати точності внаслідок браку значущих цифр.

Під час перетворення цілих чисел на дробові можуть відкидатися наймолодші розряди числа. Але оскільки суть дробового числа в тому, щоб зберігати приблизне значення, таке присвоєння дозволено.


3. Звуження типів

А що там з іншими варіантами? Що робити, коли змінній типу int потрібно присвоїти значення змінної типу long?

Уявіть, що змінна — це кошик. Маємо кошики різного розміру: 1, 2, 4 і 8 байтів. Якщо перекладати пиріжки з меншого кошика в більший, проблем не виникає. А от якщо перекладати пиріжки з більшого кошика в менший, частина пиріжків може не вміститися.

Таке перетворення — від типу більшого розміру до меншого — називають звуженням типу. У разі такого присвоювання частина числа може просто не вміститися в нову змінну і «залишитися за бортом».

Під час звуження типу потрібно явно показати компілятору, що ми не помилилися, і відкидаємо частину числа свідомо. Для цього використовується оператор перетворення типу: це ім'я типу в круглих дужках.

У таких ситуаціях Java-компілятор вимагає від програміста вказувати оператор перетворення типу. Загальний вигляд цього оператора такий:

(тип) вираз

Приклади:

Код Опис
long a = 1;
int b = (int) a;
short c = (short) b;
byte d = (byte) c;
Щоразу потрібно явно вказувати оператор перетворення типу

У цьому випадку a дорівнює 1, і це здається зайвим. А якби a мало більше значення?

Код Опис
long a = 1000000;
int b = (int) a;
short c = (short) b;
byte d = (byte) c;
a == 1000000
b == 1000000
c == 16960
d == 64

Мільйон чудово вміщується і в тип long, і в тип int. А от під час присвоювання мільйона змінній типу short два перших байти було відкинуто, і залишилося тільки два останніх. А коли спробували записати мільйон у змінну типу byte, взагалі залишився тільки один останній байт.

Будова чисел у пам'яті:

Тип Двійковий запис Десятковий запис
int 0b00000000000011110100001001000000 1,000,000
short 0b0100001001000000 16,960
byte 0b01000000 64

Тип char

Тип char, як і тип short, займає в пам'яті два байти, але для перетворення одного з цих типів на інший завжди потрібно використовувати оператор перетворення типу. Річ у тім, що тип short — знаковий і може містити значення від -32768 до +32767, а тип char — беззнаковий і може містити значення від 0 до 65535.

У змінних типу char не можна зберігати від'ємні числа, які можуть зберігатися в змінних типу short. А в змінних типу short не можна зберігати числа, більші за 32767, які можуть зберігатися у змінних типу char.


4. Тип виразу

А що робити, якщо в одному виразі використовуються змінні різних типів? Логічна відповідь — їх спочатку потрібно перетворити на один спільний тип. Але який?

Звісно ж, на тип більшого розміру.

У Java завжди відбувається перетворення на тип більшого розміру. У загальних рисах, спочатку відбувається розширення типу одного з членів операції, а вже потім — операція зі значеннями однакового типу.

Якщо у виразі є типи int і long, значення типу int буде перетворено на тип long, і тільки після цього його можна буде використовувати в операції:

Код Опис
int a = 1;
long b = 2;
long c = a + b;
a буде розширено до типу long, і лише потім буде виконано додавання.

Числа з рухомою крапкою

Якщо вираз містить ціле число й число з рухомою крапкою (float/double), ціле число буде перетворено на число з рухомою крапкою (float/double), і тільки потім буде виконано операцію з ними.

Якщо потрібно виконати операцію з числами float і double, то число типу float буде перетворено на тип double. Що, власне, й очікувалось.

Сюрприз

Типи byte, short, char під час взаємодії завжди перетворюються на тип int. Недарма ж тип int вважається стандартним цілочисловим типом.

Якщо помножити число типу byte на число типу short, отримаємо число типу int. Якщо помножити число типу byte на число типу byte, отримаємо число типу int. Навіть якщо скласти число типу byte і число типу byte, знову таки отримаємо число типу int.

Для цього є кілька причин. Приклади:

Код Опис
byte a = 110;
byte b = 120;
byte c = a * b;   // помилка
110 * 120 буде 13200, це дещо перевищує максимальне значення типу byte: 127
byte a = 110;
byte b = 120;
byte c = a + b;   // помилка
110 + 120 буде 230, це теж дещо перевищує максимальне значення типу byte: 127

У загальному випадку результатом множення числа довжиною 8 бітів (1 байт) на число довжиною 8 бітів (1 байт) буде число довжиною 16 бітів (2 байти).

Тому в усіх операціях з цілими типами, меншими за int, ці типи завжди відразу перетворюються на тип int. І якщо ви захочете зберегти результат обчислення у змінній, тип якої менший за int, вам завжди доведеться явно вказувати операцію перетворення типу.

Приклади:

Код Опис
byte a = 110;
byte b = 120;
byte c = (byte) (a * b);
вираз byte * byte матиме тип int
byte a = 110;
byte b = 120;
byte c = (byte) (a + b);
вираз byte + byte матиме тип int
byte a = 1;
byte b = (byte) (a + 1);
вираз byte + int матиме тип int
одиниця — це літерал типу int.

5. Важливий момент

Операція перетворення типу має досить високий пріоритет.

Тому якщо у виразі є, скажімо, операції додавання й перетворення типу, то перетворення типу буде виконано перед додаванням.

Приклад:

Код Опис
byte a = 1;
byte b = 2;
byte c = (byte) a * b;
Оператор перетворення типу буде застосовано тільки до змінної a, яка й без того має тип byte. Код не скомпілюється.
byte a = 1;
byte b = 2;
byte c = (byte) (a * b);
А отак правильно.

Якщо ви хочете перетворити на певний тип увесь вираз, а не тільки один із його елементів, візьміть увесь вираз в круглі дужки і поставте перед ним оператор приведення типу.