При рассмотрении типов данных указывалось, какие значения может иметь тот или иной тип и сколько байт памяти он может занимать. И мы можем написать, например, так:
1 2 |
byte x = 5; byte y = x; |
Но важно понимать, что это запись не эквивалентна следующей (хотя результат будет тот же):
1 2 |
byte x = 5; int y = x; |
В обоих случаях создается переменная типа
byte
, которая затем приравнивается другой переменной. Однако если в первом случае это простое приравнивание, а переменная y просто получает значение переменной x, то во втором примере происходит преобразование типов: данные типа byte
преобразуются к типу int
. Данный тип преобразований называется расширяющим, так как значение типа byte расширяет свой размер до размера типа int. Расширяющие преобразования проходят автоматически и обычно с этим никаких проблем не возникает.
Подобным образом происходит преобразование от типа float
к типу double
или от типа int
к типу long
.
Кроме расширяющих преобразований есть еще и сужающие. Сужающие преобразования позволяют привести данные к типу с меньшей разрядностью, например, от типа int
, который занимает 4 байта в памяти, к типу byte
, который занимает только 1 байт в памяти:
1 2 |
int a = 4; byte b = a; |
Несмотря на то, что значение переменной a — число 4 укладывается в диапазон типа byte
, мы все равно получим ошибку. И чтобы безошибочно провести преобразование из одного типа к другому, нам надо применить операцию приведения типов. Суть этой операции состоит в том, что в скобках указывается тип, к которому надо привести данное значение:
1 2 |
int a = 4; byte b = (byte) a; |
Потеря данных при преобразовании
В предыдущей ситуации число 4 вполне укладывалось в диапазон значений типа byte. Но что будет в следующем случае:
1 2 |
int a = 200; byte b = (byte) a; |
Результатом будет число -56. В данном случае число 200 вне диапазона для типа byte (от -128 до 127), поэтому произойдет усечение значения. Так как тип byte предполагает 256 возможных значений, то полученное значение будет равно 200-256, то есть -56.
Усечение рациональных чисел до целых
При преобразовании значений с плавающей точкой к целочисленным значениям, происходит усечение дробной части:
1 2 |
double a = 56.9898; int b = (int)a; |
Здесь значение числа b будет равно 56, несмотря на то, что число 57 было бы ближе к 56.9898. Чтобы избежать подобных казусов, надо применять функцию округления, которая есть в математической библиотеке Java:
1 2 |
double a = 56.9898; int b = (int)Math.round(a); |
Преобразования при операциях
Нередки ситуации, когда приходится применять различные операции, например, сложение и произведение, над значениями разных типов. Здесь также действуют некоторые правила:
- если один из операндов операции относится к типу
double
, то и второй операнд преобразуется к типуdouble
- если предыдущее условие не соблюдено, а один из операндов операции относится к типу
float
, то и второй операнд преобразуется к типуfloat
- если предыдущие условия не соблюдены, один из операндов операции относится к типу
long
, то и второй операнд преобразуется к типуlong
- иначе все операнды операции преобразуются к типу
int
Например:
1 2 3 |
int a = 3; double b = 4.6; double c = a+b; |
Так как в операции участвует значение типа double, то и другое значение приводится к типу double и сумма двух значений a + b
будет представлять тип double.
Другой пример:
1 2 3 |
byte a = 3; short b = 4; byte c = (byte)(a+b); |
Две переменных типа byte и short (не double, float или long), поэтому при сложении они преобразуются к типу int
, и их сумма a+b
представляет значение типа int. Поэтому если затем мы присваиваем эту сумму переменной типа byte, то нам опять надо сделать преобразование типов к byte.