Appearance
Приведение типов
Как уже обсуждалось ранее, 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)
Ещё раз, автоматическое преобразование срабатывает при соблюдении двух условий:
- Оба типа совместимы (чисто физически не получится преобразовать "abc" в
int); - Целевой тип больше исходного (
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, и программа упадёт.