Иногда данные из одного формата требуется преобразовать в другой. В данной статьей мы рассмотрим способы конвертирования типа данных.
Содержание статьи
Обратите внимание, что попытка выполнить конвертацию типа следующим образом обернется ошибкой:
1 2 3 | var integer: Int = 100 var decimal: Double = 12.5 integer = decimal |
Текст ошибки, которую выдает Kotlin при попытке выполнить преобразование таким образом:
1 | Type mismatch: inferred type is Double but Int was expected |
Некоторые языки программирования менее строгие и выполняют конвертацию подобного рода автоматически. Опыт показывает, что такое автоматическое преобразование является источником ошибок в программах и зачастую снижает производительность.
Kotlin запрещает присваивать значение одного типа другому и тем самым помогает избежать проблем.
Помните, что именно программисты заставляют компьютеры работать. В Kotlin частью задачи разработчика является явное преобразование типов. Если вам нужно преобразование, вы должны сообщить программе об этом сами!
Вместо простого присваивания требуется явно сообщить о необходимости преобразования типа. Это делается следующим образом:
1 | integer = decimal.toInt() |
Теперь присваивание однозначно сообщает Kotlin, что вы хотите преобразовать исходный тип Double
в новый тип Int
.
На заметку: В данном случае присваивание десятичного значения целому числу приводит к потере точности: переменная
integer
получает значение12
вместо12.5
. Именно по этой причине важно работать явно. Kotlin хочет убедиться, что вы знаете, что делаете, и что вы можете потерять данные, выполняя преобразование типов.
Операции со смешанными типами в Kotlin
До сих пор вы видели только операторы, действующие независимо от целых или десятичных чисел. Но что, если у вас есть целое число, которое требуется умножить на десятичное?
У вас может возникнуть идея сделать что-то вроде этого:
1 2 3 | val hourlyRate: Double = 19.5 val hoursWorked: Int = 10 val totalCost: Double = hourlyRate * hoursWorked.toDouble() |
В данном примере константа hoursWorked
явно конвертируется в тип Double
для совпадения с типом константы hourlyRate
. Однако, делать это нет нужды. Kotlin позволяет умножать эти значения без всякой конвертации следующим образом:
1 | val totalCost: Double = hourlyRate * hoursWorked |
В Kotlin оператор *
можно использовать со смешанным типам . Это правило также применяется к другим арифметическим операторам. Несмотря на то, что hoursWorked
является типом Int
, это не повлияет на точность hourlyRate
. Результатом все равно будет 195.0
!
Kotlin старается быть как можно более кратким и пытается вывести ожидаемое поведение, когда это возможно. Таким образом, вы тратите меньше времени на заботу о типах и больше на создание интересных программ!
Вывод типов данных в Kotlin (Type inference)
До сих пор каждое объявление переменной или константы сопровождалась объявлением типа. Может возникнуть вопрос, зачем писать : Int
и : Double
, ведь правая часть присваивания уже является Int
или Double
. Разумеется, это излишне, это ведь и так понятно.
Оказывается, компилятору Kotlin это тоже понятно. Ему не нужно постоянно указывать тип — он может определить его самостоятельно. Это делается с помощью процесса, который называется выводом типа. Это ключевой компонент мощности Kotlin, который есть далеко не в каждом языке программирования.
Таким образом, вы можете просто не указывать тип в большинстве мест, где вы его видите.
К примеру, рассмотрим следующее объявление константы:
1 | val typeInferredInt = 42 |
Иногда будет полезно проверить предполагаемый тип переменной или константы. Вы можете сделать это в IntelliJ, нажав на название переменной или константы и удерживая клавиши Control + Shift + P. IntelliJ отобразит всплывающее окно, наподобие следующего:
IntelliJ показывает предполагаемый тип, предоставляя вам объявление, которое вам пришлось бы использовать, если бы не было вывода типа (type inference). В данном случае это тип Int
.
Для других типов это также работает:
1 | val typeInferredDouble = 3.14159 |
При удерживании клавиш Control + Shift + P будет показано следующее:
Из данных примеров видно, что в выводе типов никакой магии нет. Kotlin просто делает то же самое, что и ваш мозг. Языки программирования без вывода типов часто могут показаться многословными, потому что зачастую нужно указывать очевидный тип каждый раз при объявлении переменной и константы.
На заметку: В следующих уроках вы узнаете о более сложных типах данных, для которых Kotlin не всегда может определить тип автоматически. Однако это довольно редкий случай, вывод типа используется для большинства примеров кода данного курса — за исключением случаев, когда вы хотите указать тип самостоятельно.
В некоторых случаях требуется определить константу или переменную и убедиться в их типе, даже если вы назначаете другой тип. Ранее вы видели, как можно преобразовать один тип в другой. Например, рассмотрим следующий пример:
1 | val wantADouble = 3 |
Здесь Kotlin выберет Int
в качестве типа константы wantADouble
. Однако, что если вам нужен вместо Int
тип Double
?
Сначала требуется сделать следующее:
1 | val actuallyDouble = 3.toDouble() |
Это похоже на преобразование типа, которое вы видели ранее.
Другой опцией стал бы отказ от использования вывода типов вообще:
1 | val actuallyDouble: Double = 3.0 |
Быть может, вам захочется сделать следующее:
1 | val actuallyDouble: Double = 3 |
Этого делать нельзя, компилятор выведет следующую ошибку: The integer literal does not conform to the expected type Double
.
На заметку: В Kotlin литеральные значения, такие как
3
, имеют определенный тип —Int
. Если вы хотите преобразовать их в другой тип, вы должны сделать это явно, например, вызвавtoDouble()
. Литеральное числовое значение, содержащее десятичную точку, может использоваться только какDouble
и должно быть явно преобразовано, если вы хотите использовать его как что-то еще. По этой причине нельзя присвоить значение3
константеactualDouble
.Точно так же литеральные числовые значения, которые содержат десятичную точку, не могут быть целыми числами integer. Это означает, что вы могли бы избежать всего этого обсуждения, если бы начали со строчки:
val wantADouble = 3.0
Извиняемся!
Вопросы для проверки
1. Создайте константу под названием age1
, которая равна 42
. Создайте константу age2
, которая равна 21
. Проверьте, используя Control + Shift + P
, что тип обоих был правильно выведен как Int
;
2. Создайте константу avg1
, ее значением должно быть среднее арифметическое age1
и age2
, которое находится через простую операцию (age1 + age2) / 2
. Используйте Control + Shift + P, чтобы проверить тип и проверить результат avg1
. Что не так?
3. Исправьте ошибку в приведенном выше упражнении, преобразовав age1
и age2
в тип Double
. Используйте Control + Shift + P, чтобы проверить тип и результат avg1
. Почему теперь все правильно?