Преобразование и приведение примитивных типов
В этом уроке мы поговорим с Вами о преобразовании примитивных типов в Java.
Когда будет происходить такое преобразование? Иногда возникают ситуации, когда необходимо переменной одного типа присвоить значение переменной другого типа. Например:
int i = 11;
byte b = 22;
i = b;
Конечно же в этом случае произойдёт преобразование типа - из типа byte число преобразуется в тип int. Либо обратный вариант - из типа int число преобразуется в тип byte:
int i = 11;
byte b = 22;
b = (byte)i;
Давайте посмотрим, что будет происходить в обоих из этих вариантов.
В первом случае мы преобразуем из типа byte в тип int. То есть из маленького типа в большой тип. Это будет безопасное преобразование, которое произойдёт без проблем. В двоичном виде это будет выглядеть таким образом:
Значение типа byte сверху - это 8 бит. Значение типа int снизу - это будет 32 бита. Значение в типе int просто перезаписывается и в старших битах недостающие значения просто заменяться ноликами. Такой тип преобразования называется автоматическим преобразованием.
Но что будет во втором варианте, когда мы делаем обратное преобразование из типа int в тип byte?
int i = 11;
byte b = 22;
b = (byte)i;
Здесь мы указываем, что мы явно преобразуем к типу byte. Если этого не сделать, то произойдет ошибка компиляции. Такое преобразование не всегда является безопасным. Если преобразуется какое-то небольшое число, которое входит в диапазон типа int, тогда преобразование произойдёт без проблем. Но если преобразовывать число, которое не входят в диапазон типа byte, допустим число 320, то посмотрите, что здесь произойдёт:
Те значения, которые не поместятся в byte, просто отбросятся и в результате получится какое-то другое число. Здесь даже не произойдёт округление. В результате приведения числа 320 в byte получится число 64.
Такое преобразование называется приведением и такое преобразование не является безопасным. Именно поэтому в скобочках мы указываем, что мы приводим к типу byte. Таким образом мы говорим компилятору, что да мы понимаем, что здесь возможна потеря данных.
Итак, в Java существует два типа преобразований - автоматическое преобразование (неявное) и приведение типов (явное преобразование).
1. Автоматическое преобразование типов Java
Рассмотрим сначала автоматическое преобразование. Если оба типа совместимы, их преобразование будет выполнено в Java автоматически. Например, значение типа byte
всегда можно присвоить переменной типа int, как это показано в предыдущем примере.
Такое преобразование является безопасным и ещё оно называется неявное преобразование. Неявное, потому что мы явно не указываем, что необходимо преобразовать число. Ещё автоматическое преобразование называется расширяющим преобразованием, потому что мы расширяем наше значение из маленького к большому.
Для автоматического преобразования типа должно выполняться два условия:
- оба типа должны быть совместимы
- длина целевого типа должна быть больше длины исходного типа
В этом случае происходит преобразование с расширением.
Но что такое совместимые типы? Допустим, все числовые типы совместимы между собой. Но значение типа boolean и числовой тип, конечно же, несовместимы между собой. Также какое-то строковое число "Hello" мы не можем привести к числовому типу.
Следующая схема показывает расширяющее преобразование в Java:
Сплошные линии обозначают преобразования, выполняемые без потери данных. Штриховые линии говорят о том, что при преобразовании может произойти потеря точности.
Например, тип данных int
всегда достаточно велик, чтобы хранить все допустимые значения типа byte
, поэтому никакие операторы явного приведения типов в данном случае не требуются. С точки зрения расширяющего преобразования числовые типы данных, в том числе целочисленные и с плавающей точкой, совместимы друг с другом. В то же время не существует автоматических преобразований числовых типов в тип char
или boolean
. Типы char
и boolean
также не совместимы друг с другом.
Стоит немного пояснить почему, к примеру тип byte
не преобразуется автоматически (неявно) в тип char, хотя тип byte
имеет ширину 8 бит, а char
- 16. То же самое касается и преобразования типа short
в char
. Это происходит потому, что byte
и short
знаковые типы данных, а char
беззнаковый. Поэтому в данном случае требуется использовать явное приведение типов, поскольку компилятору надо явно указать, что вы знаете чего хотите и как будет обрабатываться знаковый бит типов byte
и short
при преобразовании к типу char
.
Поведение величины типа char в большинстве случаев совпадает с поведением величины целого типа, следовательно, значение типа char
можно использовать везде, где требуются значения int
или long
. Однако напомним, что тип char
не имеет знака, поэтому он ведет себя отлично от типа short
, несмотря на то что диапазон обоих типов равен 16 бит.
2. Приведение типов Java
Несмотря на все удобство автоматического преобразования типов, оно не в состоянии удовлетворить все насущные потребности. Например, что делать, если значение типа int
нужно присвоить переменной типа byte
? Это преобразование не будет выполняться автоматически, поскольку длина типа byte
меньше, чем у типа int
. Иногда этот вид преобразования называется сужающим преобразованием, поскольку значение явно сужается, чтобы уместиться в целевом типе данных.
Чтобы выполнить преобразование двух несовместимых типов данных, нужно воспользоваться приведением типов. Приведение - это всего лишь явное преобразование типов. Общая форма приведения типов имеет следующий вид:
(целевой_тип) значение
Где параметр целевой_тип
обозначает тип, в который нужно преобразовать указанное значение.
Такое приведение еще называется явным, потому что здесь явно указывается, что мы приводим к типу byte, либо к какому-то другому типу. И оно является небезопасным.
Например, в следующем фрагменте кода тип int
приводится к типу byte
:
int i = 11;
byte b = 22;
b = (byte) i;
И дальше давайте рассмотрим разные варианты приведения типов и какие потери данных возможны при них.
Рассмотрим пример преобразования значений с плавающей точкой в целые числа. В этом примере дробная часть значения с плавающей точкой просто отбрасывается (операция усечения):
double d = 3.89;
int a = (int) d; //Результат будет 3
При приведении более емкого целого типа к менее емкому старшие биты просто отбрасываются:
int i = 323;
byte b = (byte) i; //Результат будет 67
При приведении более емкого значения с плавающей точкой в целое число происходит усечение и отбрасывание старших битов:
double d = 389889877779.89;
short s = (short) d; //Результат будет -1
3. Автоматическое продвижение типов в выражениях
Помимо операций присваивания, определенное преобразование типов может выполняться и в выражениях. Допустим нам нужно сложить два числа - один типа int и второе значение типа byte. Вопрос - какое значение у нас будет иметь сумма?
В языке Java действуют следующие правила:
- Если один операнд имеет тип
double
, другой тоже преобразуется к типуdouble
. - Иначе, если один операнд имеет тип
float
, другой тоже преобразуется к типуfloat
. - Иначе, если один операнд имеет тип
long
, другой тоже преобразуется к типуlong
. - Иначе оба операнда преобразуются к типу
int
. - В выражениях совмещенного присваивания (+=,-=, *=, /=) нет необходимости делать приведение.
Приведем пример:
При умножении переменной b1
(byte
) на 2 (int
) результат будет типа int
. Поэтому при попытке присвоить результат в переменную b2
(byte
) возникнет ошибка компиляции. Но при использовании совмещенной операции присваивания (*=), такой проблемы не возникнет:
byte b1 = 1;
byte b2 = 2 * b1; //Ошибка компиляции
int i1 = 2 * b1;
b2 *= 2;
В следующем примере тоже возникнет ошибка компиляции - несмотря на то, что складываются числа типа byte
, результатом операции будет тип int
, а не short
.
public class IntegerExample1 {
public static void main(String[] args) {
byte b1 = 50, b2 = -99;
short k = b1 + b2; //ошибка компиляции
System.out.println("k=" + k);
}
}
Следующий пример аналогичен предыдущему, но используется операция совмещенного присваивания, в которой приведение происходит автоматически:
public class IntegerExample2 {
public static void main(String[] args) {
byte b1 = 50, b2 = -99;
b1 += b2;
System.out.println("b1=" + b1);
}
}
Презентацию с видео можно скачать на Patreon.
Курс 'Java для начинающих' на Udemy
Зарегистрируйтесь или войдите, чтобы иметь возможность оставить комментарий.