Сегодня поговорим о примитивах:
Большинство Java-разработчиков уверены, что прекрасно знают примитивы.
Но есть одна особенность языка, на которой регулярно ошибаются даже опытные разработчики.
Посмотрите на код:
byte a = 10;
byte b = 20;
byte c = a + b;
Как думаете, скомпилируется?
Нет.
Компилятор выдаст ошибку:
incompatible types: possible lossy conversion from int to byte
Но почему? - Ведь мы складываем два byte.
Дело в том, что в Java существует механизм numeric promotion.
При выполнении арифметических операций значения типов byte, short и char автоматически расширяются до int.
То есть выражение:
a + b
фактически имеет тип int, а не byte.
Условно JVM рассматривает это как:
(int) a + (int) b
Получается:
10 + 20 = 30
но результат этого выражения уже является int.
И когда мы пытаемся сделать:
byte c = a + b;
происходит попытка положить более широкий тип int в более узкий byte.
Java не может гарантировать, что значение помещается в диапазон byte:
byte: -128 ... 127
int: -2147483648 ... 2147483647
Поэтому требуется явное приведение:
byte c = (byte) (a + b);
Но важно понимать:
Это не означает, что все примитивы "под капотом становятся int".
Переменная:
byte value = 10; - остается типа byte.
Расширение происходит только во время выполнения арифметической операции.
Еще один пример:
char ch = 'A';
System.out.println(ch + 1);
Результат:
66
Почему не B?
Потому что char в Java хранит числовое значение Unicode.
Символ:
'A' = 65
Во время вычисления происходит расширение:
(int)'A' + 1
Получаем:
65 + 1 = 66
Поэтому результат — число.
А теперь интересный момент:
Почему этот код компилируется?
char c = 'A';
c += 1;
System.out.println(c);
Вывод:
B
Хотя такой код уже не скомпилируется:
char c = 'A';
c = c + 1;
Почему?
Потому что составной оператор присваивания работает немного иначе.
Выражение:
c += 1;
Java автоматически преобразует примерно в:
c = (char) (c + 1);
То есть внутри происходит:
- char расширяется до int;
- выполняется сложение;
- результат автоматически приводится обратно к типу левой части (char).
А вот в обычном присваивании:
c = c + 1;
результат выражения:
c + 1
имеет тип int.
И Java не делает неявное сужение: int → char
потому что это потенциально может привести к потере данных.
Именно поэтому:
byte b = 1;
b += 1; // работает
b = b + 1; // ошибка компиляции
На первый взгляд оба выражения выглядят одинаково.
Но для Java это разные операции. И такие маленькие детали часто становятся границей между разработчиком, который просто пишет код, и инженером, который понимает, что происходит под капотом.