Skip to content
Содержание

Приведение типов

Как уже обсуждалось ранее, Java - язык со строгой типизацией: каждая переменная имеет определённый тип, и компилятор следит, чтобы мы не смешивали несовместимые типы. Но часто бывает нужно преобразовать значение одного типа в другой. Например, целое число превратить в дробное, строку - в число, и наоборот. В этой лекции будут рассмотрены два вида преобразования (оно же приведение типов).

Виды приведения

Неявное приведение (автоматическое)

Неявное приведение выполняется компилятором автоматически, так как это безопасно и нет риска потерять данные. Неявные приведения происходят, когда мы присваиваем значение "меньшего" типа переменной "большего" типа. Например, int можно присвоить double, потому что double может хранить все целые значения int и плюс дробную часть.

java
int age = 25;
double preciseAge = age; // неявное преобразование int -> double
System.out.println(preciseAge); // 25.0

Цепочка безопасных расширений, которые выполняются автоматически: byte -> short -> int -> long -> float -> doublechar -> int

Такие преобразования называются расширяющими, т.е. мы "расширяем" тип данных переменной.

То есть можно без проблем писать:

java
long big = 100;       // int -> long
float pi = 3.14f;     // float
double d = pi;        // float -> double (расширяем float до double)

А что за f в числе 3.14f из примера выше?

Я на этом внимания не акцентировал в первой лекции (и конечно же не потому, что я забыл. Честно.), но так мы обозначаем число типа float. Просто дробное число без литерала f (например 3.14) по умолчанию считается типом double. Поэтому, код вида:

java
float x = 3.14;

вызовет ошибку компиляции. Потому что 3.14 имеет тип данных double, который "больше", чем float. Из-за этого неявного преобразования не происходит. А литерал f после числа явно говорит компилятору, что это число имеет тип данных float. То же самое и с типом данных long. По умолчанию целое число считается типом данных int, поэтому нам надо явно указать литерал L (заглавная буква! (можно и маленькую, но это моветон)):

java
// Тут 100 помещается в диапазон int (от -2,147,483,648 до 2,147,483,647)
// поэтому литерал ставить необязательно
long x = 100; 

// Но если число не помещается в диапазон инта, то литерал обязателен!
long x = 3000000000; // Число слишком большое для int, будет ошибка компиляции
long x = 3000000000L; // Явно указали литерал, всё будет работать

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

java
int a = 5;
double b = 2.5;
double result = a + b; // a преобразуется в double, результат double (7.5)

Ещё раз, автоматическое преобразование срабатывает при соблюдении двух условий:

  1. Оба типа совместимы (чисто физически не получится преобразовать "abc" в int);
  2. Целевой тип больше исходного (int -> long - да, long -> int - нет).

При соблюдении этих условий происходит расширяющее преобразование.

Явное преобразование (каст)

Если мы хотим преобразовать значение "большего" типа в "меньший", мы должны сделать это явно, потому что возможна потеря данных. Для этого перед значением в скобках указываем целевой тип:

java
double pi = 3.14159;
int intPi = (int) pi; // явное преобразование double -> int
System.out.println(intPi); // 3 - дробная часть отброшена!

Информация сомнительной полезности

Потеря дробной части - не округление, а просто отбрасывание всего после запятой.

Другой пример:

java
long bigNumber = 10000000000L;
int small = (int) bigNumber; // может переполниться и дать неверное значение
System.out.println(small); // тут будет мусор

Почему вообще Java требует явных преобразований? Почему нельзя просто всегда делать автоматические, чтобы я не мучался с этими вашими кастами? Потому что в больших проектах за потоком данных уследить сложно. И тем более сложно понять почему у вас в какой-то момент единица превратилась в... кота, например? Явное преобразование - это договор об отсутствии претензий, который вы, как разработчик, подписываете. "Я понимаю, что могу потерять данные и беру всю ответственность на себя".

Преобразование чисел в строки

Очень часто нужно склеить число с текстом. В Java при использовании оператора + со строкой любой тип автоматически преобразуется в строку:

java
int count = 10;
String message = "Количество: " + count; // int -> String неявно
System.out.println(message); // Количество: 10

Если нужно явно преобразовать число в строку, можно использовать String.valueOf():

java
double price = 19.99;
String priceStr = String.valueOf(price); // "19.99" - строка

Преобразование строк в числа

Обратное преобразование - из строки в число - не является автоматическим. Для этого используют методы-парсеры классов-обёрток (вы не поверите, но обёртки мы тоже будем проходить потом. Пока просто запоминаем синтаксис):

java
String input = "42";
int number = Integer.parseInt(input); // строка -> int
double d = Double.parseDouble("3.14");
long l = Long.parseLong("1000000");

Если строка содержит не число, например "abc", то будет выброшено исключение NumberFormatException, и программа упадёт.