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

Змінні примітивних типів (за винятком типу boolean) використовуються для зберігання різних типів чисел. І хоч типи змінних завжди незмінні, є місце, де можна проводити перетворення типів. І це місце — присвоєння.
Можна присвоювати один одному змінні різних типів. При цьому значення, взяте зі змінної одного типу, буде перетворене у значення іншого типу і присвоєне другій змінній. У зв'язку з цим можна виділити два види перетворення типів: розширення і звуження.
Розширення типу схоже на перекладання із маленького кошика в великий — операція проходить непомітно і безболісно. Звуження типу — це перекладання із великого кошика в маленький: місця може не вистачити, і щось доведеться викинути.
Ось типи, відсортовані за розміром «кошика»:

2. Розширення типів
Часто може виникнути необхідність присвоїти змінній одного числового типу значення змінної іншого числового типу. Як це зробити?
У Java є 4 цілих типи:
| Тип | Розмір |
|---|---|
byte |
1 байт |
short |
2 байти |
int |
4 байти |
long |
8 байтів |
Змінній більшого розміру завжди можна присвоювати значення змінних меншого розміру.
Змінній типу long спокійно можна присвоювати змінні типу int, short і byte. Змінній типу int можна присвоювати змінні типу short і byte. Ну і змінній типу short можна присвоювати змінні типу byte.
Приклади:
| Код | Опис |
|---|---|
|
Цей код прекрасно скомпілюється. |
Таке перетворення, від типу меншого розміру до більшого, називається розширенням типу.
А що стосовно дійсних чисел?
З ними все аналогічно — розмір має значення:
| Тип | Розмір |
|---|---|
float |
4 байти |
double |
8 байтів |
Змінній типу double можна без проблем присвоїти змінну типу float. А от з цілими типами цікавіше.
Змінній типу float можна присвоїти змінну будь-якого цілого типу. Навіть типу long, довжина якого 8 байтів. А змінній типу double можна присвоїти взагалі що завгодно: змінну будь-якого цілого типу і змінну типу float:
| Код | Примітка |
|---|---|
|
|
Зверни увагу, що перетворення до дійсного типу може призвести до втрати точності через нестачу значущих цифр.
При перетворенні з цілих чисел у дробові можуть відкидатися найменші частини числа. Але оскільки сенс дробового числа в тому, щоб зберігати приблизне значення, таке присвоєння дозволяється.
3. Звуження типів
А що щодо інших варіантів? Що робити, якщо потрібно змінній типу int присвоїти значення змінної типу long?
Уявіть, що змінна — це кошик. У нас є кошики різних розмірів: 1, 2, 4 і 8 байт. При перекладанні пиріжків із меншого кошика у більший проблем не буде. А ось при перекладанні з більшого в менший частина пиріжків може загубитися.
Це перетворення — від типу більшого розміру до меншого — називають звуженням типу. При такому присвоєнні частина числа може просто не вміститися в нову змінну й «залишитися за бортом».
При звуженні типу ми маємо явно показати компілятору, що ми не помилилися, і відкидання частини числа зроблене усвідомлено. Для цього використовується оператор приведення типу. Це ім'я типу в круглих дужках.
У таких ситуаціях Java-компілятор вимагає від програміста вказувати оператор перетворення типу. Загалом він виглядає так:
(тип) вираз
Приклади:
| Код | Опис |
|---|---|
|
Кожного разу потрібно явно вказувати оператор перетворення типу |
У цьому випадку a дорівнює 1, і це здається надмірним. А що, якби a було більше?
| Код | Опис |
|---|---|
|
|
Мільйон чудово вміщується і в тип long, і в тип int. А ось при присвоєнні мільйона змінній типу short два перших байти були відкинуті, і залишилися тільки два останні байти. А при присвоєнні типу byte взагалі залишився один останній байт.
Устрій чисел у пам'яті:
| Тип | Двійковий запис | Десятковий запис |
|---|---|---|
int |
0b00000000000011110100001001000000 | 1,000,000 |
short |
0b0100001001000000 | 16,960 |
byte |
0b01000000 | 64 |
Тип char
Тип char, як і тип short, займає два байти, але для їх перетворення одного в одного завжди потрібно використовувати оператор приведення типу. Усе тому, що тип short знаковий, і може зберігати значення від -32,768 до +32,767, а тип char беззнаковий, і може зберігати значення від 0 до 65,535.
У char не можна зберегти від'ємні числа, які можуть зберігатися в short. А в short не можна зберегти числа більше 32,767, які можуть зберігатися в char.
4. Тип виразу
А що робити, якщо в одному виразі використовуються змінні різних типів? Логічна відповідь – їх спочатку потрібно перетворити до загального типу. Але до якого?
Звичайно ж, до більшого.
У Java завжди відбувається перетворення до типу більшого розміру. Грубо кажучи, спочатку відбувається розширення типу одного з учасників операції, а вже потім операція зі значеннями однакових типів.
Якщо у виразі беруть участь типи int та long, значення типу int буде перетворено до типу long і лише потім братиме участь в операції:
| Код | Опис |
|---|---|
|
a буде розширено до типу long і тільки тоді відбудеться додавання. |
Числа з плаваючою точкою
Якщо у виразі беруть участь ціле число і число з плаваючою точкою (float/double), ціле число буде перетворено в число з плаваючою точкою (float/double) і тільки потім буде виконано операцію над ними.
Якщо в операції беруть участь float та double, то float буде перетворено до double. Що, власне, очікувано.
Типи byte, short, char завжди перетворюються в тип int при взаємодії між собою. Не дарма ж тип int вважається стандартним цілочисельним типом.
Якщо перемножити byte на short, буде int. Якщо перемножити byte на byte, буде int. Навіть якщо скласти byte і byte, буде int.
Причин цьому декілька. Приклади:
| Код | Опис |
|---|---|
|
110 * 120 буде 13,200, що дещо більше, ніж максимальне значення типу byte: 127 |
|
110 + 120 буде 230, що також дещо більше, ніж максимальне значення типу byte: 127 |
У загальному випадку при множенні числа довжиною в 8 біт (1 байт) на число довжиною в 8 біт (1 байт), ми отримаємо число довжиною 16 біт (2 байти)
Тому всі операції з цілочисельними типами, меншими ніж int, завжди одразу перетворюються в тип int. І тому якщо ви захочете зберегти результат обчислення в змінну типу, меншого ніж int, вам завжди потрібно буде явно вказувати операцію приведення типу.
Приклади:
| Код | Опис |
|---|---|
|
вираз byte * byte буде мати тип int |
|
вираз byte + byte буде мати тип int |
|
вираз byte + int буде мати тип intодиниця – це літерал типу int. |
5. Важливий нюанс
Операція приведення типу має доволі високий пріоритет.
Тому, якщо у виразі є, скажімо, додавання та операція приведення типу, вона буде виконана перед додаванням.
Приклад:
| Код | Опис |
|---|---|
|
Оператор приведення типу буде застосований лише до змінної a, яка й так має тип byte. Код не скомпілюється. |
|
Ось так правильно. |
Якщо ви хочете перетворити до певного типу увесь вираз, а не лише один його елемент, то обгорніть увесь вираз у круглі дужки та перед ними поставте оператор приведення типу.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ