1. Введение
Java является типизированным языком, что означает, что он использует концепцию типов. Существуют две отдельные группы типов:
- примитивные типы данных
- абстрактные типы данных.
В этой статье мы сосредоточимся на преобразованиях примитивных типов.
2. Обзор примитивов
Первое, что нам нужно знать, это какие значения могут использоваться с примитивными типами. Существует восемь примитивных типов:
байт
– 8 бит и знаковый
короткий
– 16 бит и знаковый
char
— 16 бит и без знака, так что он может представлять символы Unicode
int
— 32 бита и со знаком
long
— 64 бита и подписанный
float
— 32 бита и со знаком
двойной
— 64 бита и со знаком
логическое значение
— не числовое, может иметь только истинные
или ложные
значения
Это не предназначено для подробного обсуждения примитивов, и мы поговорим немного больше об их деталях по мере необходимости во время преобразований.
3. Расширение примитивных преобразований
Когда нам нужно преобразовать из примитива, который проще или меньше целевого типа, нам не нужно использовать для этого какие-либо специальные обозначения:
int myInt = 127;
long myLong = myInt;
Во время расширяющего преобразования меньшее примитивное значение помещается поверх большего контейнера, что означает, что все дополнительное пространство слева от значения заполняется нулями. Это также можно использовать для перехода от группы целых чисел к группе с плавающей запятой:
float myFloat = myLong;
double myDouble = myLong;
Это возможно, потому что при переходе к более широкому примитиву информация не теряется.
4. Сужающее примитивное преобразование
Иногда нам нужно указать значение, которое больше, чем тип, используемый в объявлении переменной. Это может привести к потере информации, поскольку некоторые байты придется отбросить.
В этом случае мы должны явно выразить, что мы осведомлены о ситуации и согласны с этим, используя приведение:
int myInt = (int) myDouble;
byte myByte = (byte) myInt;
5. Расширение и сужение примитивного преобразования
Такая ситуация возникает в очень конкретном случае, когда мы хотим преобразовать byte
в char
. Первое преобразование — это расширение byte
до int
, а затем от int
оно сужается до char
.
Пример прояснит этот момент:
byte myLargeValueByte = (byte) 130; //0b10000010 -126
Двоичное представление 130 такое же, как и для -126, разница заключается в интерпретации бита сигнала. Давайте теперь конвертируем из byte
в char
:
char myLargeValueChar = (char) myLargeValueByte;
//0b11111111 10000010 unsigned value
int myLargeValueInt = myLargeValueChar; //0b11111111 10000010 65410
Представление char
является значением Unicode, но преобразование в int
показало нам очень большое значение, в котором младшие 8 бит точно такие же, как -126.
Если мы снова преобразуем его в байт
, мы получим:
byte myOtherByte = (byte) myLargeValueInt; //0b10000010 -126
Исходное значение, которое мы использовали. Если весь код начинался с символа
, значения будут другими:
char myLargeValueChar2 = 130; //This is an int not a byte!
//0b 00000000 10000010 unsigned value
int myLargeValueInt2 = myLargeValueChar2; //0b00000000 10000010 130
byte myOtherByte2 = (byte) myLargeValueInt2; //0b10000010 -126
Хотя байтовое
представление такое же, как и -126, символьное
представление дает нам два разных символа.
6. Преобразование упаковки/распаковки
В Java у нас есть класс-оболочка для каждого примитивного типа, это умный способ предоставить программистам полезные методы обработки без накладных расходов, связанных с наличием всего в виде тяжеловесной ссылки на объект. Начиная с Java 1.5 была включена возможность автоматического преобразования в/из примитива в объект и обратно, что достигается простой атрибуцией:
Integer myIntegerReference = myInt;
int myOtherInt = myIntegerReference;
7. Преобразование строк
Все примитивные типы могут быть преобразованы в String
через их классы-оболочки, которые переопределяют метод toString()
:
String myString = myIntegerReference.toString();
Если нам нужно вернуться к примитивному типу, нам нужно использовать метод синтаксического анализа, определенный соответствующим классом-оболочкой:
byte myNewByte = Byte.parseByte(myString);
short myNewShort = Short.parseShort(myString);
int myNewInt = Integer.parseInt(myString);
long myNewLong = Long.parseLong(myString);
float myNewFloat = Float.parseFloat(myString);
double myNewDouble = Double.parseDouble(myString);
boolean myNewBoolean = Boolean.parseBoolean(myString);
Единственным исключением здесь является класс символов , потому что
String
в любом случае состоит из char
, таким образом, учитывая, что, вероятно, String
состоит из одного char
, мы можем использовать метод charAt()
класса String
:
char myNewChar = myString.charAt(0);
8. Числовые акции
Для выполнения бинарной операции необходимо, чтобы оба операнда были совместимы по размеру.
Существует ряд простых правил, которые применяются:
- Если один из операндов является
двойным
, другой повышается додвойного
- В противном случае, если один из операндов является числом с
плавающей запятой
, другой повышается до числа сплавающей запятой .
- В противном случае, если один из операндов
длинный
, другой становитсядлинным .
- В противном случае оба считаются
int
Давайте посмотрим пример:
byte op1 = 4;
byte op2 = 5;
byte myResultingByte = (byte) (op1 + op2);
Оба операнда были преобразованы в int
, и результат должен быть снова преобразован в byte
.
9. Заключение
Преобразование между типами — очень распространенная задача в повседневной деятельности по программированию. Существует набор правил, определяющих способы, которыми статически типизированные языки выполняют эти преобразования. Знание этих правил может сэкономить много времени при попытке выяснить, почему определенный код компилируется или нет.
Код, использованный в этой статье, можно найти на GitHub .