Как узнать тип переменной kotlin
Перейти к содержимому

Как узнать тип переменной kotlin

  • автор:

Проверка типа в Kotlin

В Java для проверки типа можно использовать instanceof . До этого момента я думал, что в Kotlin вместо этого используется ключевое слово is. При проведении проверки по аналогии с Java в том случае, если объект не принадлежит к классу, то возникает ошибка Incompatible types: String and Char . Как сделать проверку объекта на принадлежность к классу, чтобы при принадлежности возвращалось true , а в противном случае false ? Спасибо. Код, в котором возникает ошибка:

fun main() < var letter = '3' if (letter is String)< //Ошибка при компиляции: Incompatible types: String and Char println("String") >else println("Char") > 

Не разобрался, как провести банальную проверку:

fun main() < var letter = '3' println(letter is String) //Тоже не компилируется. Компилируется только при соответствии типов. >

Отслеживать
задан 24 авг 2021 в 19:34
faritowich faritowich
55 5 5 бронзовых знаков
24 авг 2021 в 19:44
24 авг 2021 в 20:58
Смысл в том, что тип сразу известен, попробуйте так: pl.kotl.in/QUSjpu9bN
24 авг 2021 в 21:31
обратите внимание на кавычки, в одинарных кавычках один символ определяется как char
24 авг 2021 в 21:52

0

Сортировка: Сброс на вариант по умолчанию

Знаете кого-то, кто может ответить? Поделитесь ссылкой на этот вопрос по почте, через Твиттер или Facebook.

    Важное на Мете
Похожие

Подписаться на ленту

Лента вопроса

Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.

lang-kotlin

Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.11.15.1019

Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.

Типы данных в Kotlin

В начале предыдущего урока было сказано, что данные бывают разных типов. И хотя мы присваивали переменным только строки, ничего не мешает связывать их с числами, как целыми, так и вещественными (дробными).

fun main() { val s = "Hello" val i = 2 var f = 1.34 println(s) println(i + f) // выведет 3.34 }

Kotlin относится к языкам со статической типизацией. Это значит, что у переменных есть тип, и его нельзя поменять. Их тип статичен, то есть постоянен. Например, если в приведенном выше коде мы попробуем присвоить переменной f новое значение, которое не является вещественным числом, то встроенный в IntelliJ IDEA анализатор кода сообщит об ошибке. Если мы проигнорируем и попытаемся скомпилировать проект, на ту же самую ошибку укажет уже компилятор.

Статическая типизация в Kotlin

В данном случае ошибка гласит «The integer literal does not conform to the expected type Double», что можно перевести как «Целочисленный литерал не подходит к ожидаемому типу Double». Double – это один из типов вещественных чисел. Литералами в программировании называют известные значения простых типов. IntelliJ IDEA предлагает нам исправить ошибку путем присваивания литерала 10.0 – вещественного числа с нулевой дробной частью. Однако обратим внимание на другое.

Мы нигде не указывали, что переменная f должна быть типом Double . Почему же ее посчитали таковой? На самом деле мы указали ее тип, но не явно. Тип переменной был выведен из присвоенного ей инициирующего (начального) значения. Компилятор посмотрел на число 1.34 и определил, что оно типа Double . А поскольку мы его присваиваем в том же выражении, в котором объявляем переменную, он сделал вывод, что переменная f должна быть соответствующего типа. Аналогично произошло и с другими переменными – их тип определился как String и Int . В IntelliJ IDEA чтобы увидеть тип переменной, надо установить на нее курсор и нажать Ctrl + Shift + P .

Как посмотреть тип переменной в Kotlin в IntelliJ IDEA

В большинстве других языков со статической типизацией нет таких умных компиляторов, способных выводить тип переменной, исходя из ее значения. Поэтому там тип задается явно. Мы можем явно задавать тип и в Kotlin. Однако, если присваивание идет сразу, это излишне. А вот если нет, тогда явное задание типа – единственный вариант объявления переменной.

import kotlin.random.Random fun main() { val s: String = "number: " val i: Int = Random.nextInt(1,10) val f: Double if (i > 5) f = i * 1.5 else f = i * 2.0 println(s + i) println(f) }

Явное объявление типа переменной в Kotlin и ее определение в ходе выполнения программы

Рассмотрим подробно приведенную выше программу. В Kotlin объявление переменной с явным указанием типа имеет такой синтаксис:

Схема объявления переменной в Kotlin с явным указанием типа

Переменной s сразу присваивается значение, по которому можно вывести ее тип. Поэтому явно ее тип можно было бы не указывать, о чем сообщает IntelliJ IDEA окрашивая излишества в серый цвет.

Переменной i также сразу присваивается значение, и ее тип Int можно было бы не указывать. Но, видимо, поскольку значение определяется выражением, а не литералом, среда не считает лишним явное указание типа переменной. Так код яснее.

Переменной f мы не присваиваем никакого инициирующего значения, поэтому ее тип должен быть обязательно указан явно.

Выражение Random.nextInt(1, 10) генерирует случайное целое число от 1 до 9 включительно. Слова from и until , которые вы видите на изображении, не пишутся. Их высвечивает среда для удобства, чтобы было понятно, какие параметры определены в функции nextInt .

Функция-метод nextInt принадлежит объекту Random , который мы импортируем из стандартной библиотеки Kotlin выражением import kotlin.random.Random . Слово kotlin – это имя библиотеки, random – имя пакета в ней. Пакет – это подкаталог, находящийся в каталоге kotlin , а Random – это объект, находящийся в одном из файлов пакета random . Там запрограммирована логика работы функции nextInt . Чтобы пользоваться этой функцией, понимать принцип ее работы нам не обязательно. Достаточно знать, что в нее передается, и что она возвращает.

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

Генерирование случайного целого числа с помощью объекта Random

В списке автодополнения надо выбрать то, что вам надо, и нажать Enter . Если же импорта не произошло (так бывает при вставке готового куска кода), то IntelliJ IDEA выделяет неизвестные объекты красным цветом. Надо установить курсор в выделенное слово и навести на него указатель мыши, после чего появится сообщение, предлагающее выполнить импорт нажатием Alt + Enter .

Пример импорта объекта из стандартной библиотеки Kotlin

Ниже объявления переменных в программе используется условный оператор if-else , который будет изучаться позже. Пока достаточно знать, что если значение i больше 5, то f будет присвоено значение i , умноженное на 1.5, во всех остальных случаях – умноженное на 2.

Цель этого примера показать, что первое значение переменной не обязательно присваивать сразу. Бывают ситуации, когда это происходит позже в коде. Конечно, можно там же и объявить переменную. Однако придание программному коду структуры, объявление всех используемых переменных в начале делает код более понятным. Такой код легче понимать и обслуживать.

Вернемся к рассмотрению типов данных. С учетом всех возможных библиотек, которые можно импортировать, типов-классов огромное количество. Однако выделяют так называемые базовые типы, к которым в Kotlin относят все числовые типы, булевый, символьный, а также строки и массивы. Числа, булевый тип и символьный также являются примитивными типами.

Базовые типы данных в Kotlin

Булевый, символьный и строковый типы представлены каждый одним классом (не считая типов с поддержкой null , которые мы не будем здесь рассматривать). Соответственно, Boolean , Char и String . Для чисел и массивов существует множество классов.

Так классы чисел в первую очередь подразделяются на целочисленные и вещественные. Основными классами целых являются Byte , Short , Int и Long . Вещественных классов два – Float и Double . Предел значений типа Byte – от -128 до 127 включительно, типа Long – девятнадцатизначное число. Float вмещает меньшую длину дробной части по-сравнению с Double .

Когда вы присваиваете целое или вещественное значение без явного объявления типа, они будут автоматически определены как Int или Double . Однако если целое число выходит за диапазон типа Int , лежащий в границах от -2147483648 до 2147483647, то для переменной устанавливается тип Long .

У литералов типа Float в конце обязательно должна стоять буква F или f . К литералам типа Long можно приписывать букву L .

Определение переменных типа Float и Long в Kotlin

Значениями переменных символьного типа Char являются отдельно взятые символы. Для обозначения символьного литерала используются одинарные кавычки.

fun main() { val ch = 'w' val uniChar: Char = '\u27F0' println(ch) println(uniChar) }

Символьный тип данных

Символы можно выражать через их шестнадцатеричный код по таблице символов Юникод. Так в примере выше в литерале обратный слэш ( \ ) и буква u говорят, все что далее следует интерпретировать как код символа ( 27F0 ). С помощью таких кодов можно выводить символы, которых нет на клавиатуре, в том числе весьма причудливые.

Подобные комбинации обратного слэша с определенной буквой после него называют – эскейп-последовательностями. Сочетание формирует либо особый символ, который не имеет видимого обозначения, либо экранирует обычный символ, который в языке программирования что-то значит, но его надо вывести как есть, экранировать (убрать) его значение.

Переменные булевого, или логического, типа могут принимать всего два значения. Либо true , либо false . Либо истина, либо ложь. Значения булевого типа возвращаются в результате выполнения логических выражений – обычно сравнения чего-то с чем-то, когда ответ может быть только «да» или «нет». Мы использовали одно из таких логических выражений выше в условном операторе if-else , когда сравнивали значение переменной i с числом 5.

import kotlin.random.Random fun main() { val b1: Boolean = true val b2 = false val i = Random.nextInt(10) val b3 = i > 5 println(i) println(b3) println(b1 > b2) }

Булевый тип данных

В примере переменные b1 , b2 и b3 имеют тип Boolean . Переменная i содержит случайное число от 0 до 9 включительно. Значение b3 зависит от значения i . Если значение i больше пяти, то b3 будет присвоено true , иначе – false .

В программировании истина больше лжи. Поэтому выражение b1 > b2 возвращает истину. Здесь следует обратить внимание, что сравнивать мы можем не только числа, но и объекты других типов. А вот принцип, согласно которому происходит сравнение, определяется предусмотренным для этого кодом, который находится в классе, которому принадлежит данный объект.

Так, если попробуем сравнить две строки, то они будут сравниваться лексикографически – по буквам, а не, скажем, по длине. В примере ниже, вторая буква строки «acd» больше, чем вторая буква строки «abcd», так как ‘c’ стоит дальше от начала алфавита, чем ‘b’, и имеет больший соответствующий ей числовой код.

fun main() { println("acd" > "abcd") // выведет true }

Практическая работа:

  1. Используя функцию nextDouble() объекта Random , напишите код, выводящий на экран случайное вещественное число от 0 до 1, а также случайное вещественное от -2 до 2.
  2. Разные типы данных занимают разный объем памяти. С помощью свойства SIZE_BYTES выведите на экран размер, который выделяется под каждый экземпляр целочисленных типов данных. Пример: println(Int.SIZE_BYTES) .
  3. Какой результирующий тип данных получится при сложении целого и вещественного числа, двух целых разных типов, «сложении» числа и строки?

X Скрыть Наверх

Kotlin с нуля. Курс для начинающих

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

Используйте оператор is или его отрицание !is , чтобы проверить соответствует ли объект заданному типу во время исполнения.

if (obj is String) < print(obj.length) >if (obj !is String) < // то же самое, что и !(obj is String) print("Not a String") >else

Умные приведения

В большинстве случаев вам не нужно использовать явные приведения в Kotlin, потому что компилятор отслеживает is -проверки и явные преобразования для неизменяемых значений и вставляет (безопасно) приведения автоматически, там, где они нужны.

fun demo(x: Any) < if (x is String) < print(x.length) // x автоматически преобразовывается в String >> 

Компилятор достаточно умён для того, чтобы понимать, что приведения безопасны в случаях, когда проверка на несоответствие типу ( !is ) приводит к выходу из функции:

if (x !is String) return print(x.length) // x автоматически преобразовывается в String 

или в случаях, когда приводимая переменная находится справа от оператора && или || , а соответствующая проверка (обычная или отрицательная) находится слева:

// x автоматически преобразовывается в String справа от `||` if (x !is String || x.length == 0) return // x автоматически преобразовывается в String справа от `&&` if (x is String && x.length > 0) < print(x.length) // x автоматически преобразовывается в String >

Умные приведения работают вместе с when -выражениями и циклами while :

when (x) < is Int ->print(x + 1) is String -> print(x.length + 1) is IntArray -> print(x.sum()) > 

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

  • с локальными val переменными — всегда за исключением локальных делегированных свойств.;
  • с val свойствами — если поле имеет модификатор доступа private или internal , или проверка происходит в том же модуле, в котором объявлено это свойство. Умные приведения неприменимы к публичным свойствам или свойствам, которые имеют переопределённые getter’ы;
  • с локальными var переменными — если переменная не изменяется между проверкой и использованием, не захватывается лямбдой, которая её модифицирует и не является локальным делегированным свойством;
  • с var свойствами — никогда, потому что переменная может быть изменена в любое время другим кодом.

Оператор «небезопасного» приведения

Обычно оператор приведения выбрасывает исключение, если приведение невозможно, поэтому мы называем его небезопасным. Небезопасное приведение в Kotlin выполняется с помощью инфиксного оператора as :

val x: String = y as String 

Заметьте, что null не может быть приведен к String , так как String не является nullable, т.е. если y — null, код выше выбросит исключение. Чтобы сделать этот код корректным для null-значений, используйте nullable-тип в правой части приведения.

val x: String? = y as String? 

Оператор «безопасного» (nullable) приведения

Чтобы избежать исключения, вы можете использовать оператор безопасного приведения as? , который возвращает null в случае неудачи.

val x: String? = y as? String 

Заметьте, что несмотря на то, что справа от as? стоит non-null тип String , результат приведения является nullable.

Стирание и проверка типов у Обобщений (Generics)

` is erased to just `List `. In general, there is no way to check whether an instance belongs to a generic type with certain type arguments at runtime. —>

Котлин обеспечивает типобезопасность операций, связанных с обобщениями на этапе компиляции, в то время как информация о типе аргумента обобщения недоступна во время выполнения программы. Например, для List происходит стирание типа, что превращает его в List . В связи с чем, нет способа проверить, принадлежит ли объект конкретному типу во время выполнения программы.

` or `list is T` (type parameter). You can, however, check an instance against a [star-projected type](generics.md#star-projections): —>

Учитывая это, компилятор запрещает is -проверки, которые не могут быть выполнены во время выполнения программы из-за стирания типов, например ints is List или list is T (параметризированный тип). Однако у вас есть возможность произвести проверку со «звёздными» проекциями.

if (something is List) < something.forEach < println(it) >// Элементы типа `Any?` > 

Таким же образом, когда у вас есть статически определенный тип аргумента, вы можете произвести is -проверку или приведение с необобщенной частью типа. Заметьте, что в данном случае угловые скобки пропущены:

fun handleStrings(list: List) < if (list is ArrayList) < // `list` приводится к `ArrayList` путём "умного приведения" > > 

Аналогичный синтаксис, но с пропущенным типом аргумента может использоваться для приведений, которые не принимают типы аргументы: list as ArrayList .

Встроенные (inline) функции с параметрами вещественного типа имеют свои аргументы типа, встроенные на каждый момент вызова, что позволяет arg is T проверять параметризованный тип, но если arg является объектом обобщенного типа, его аргумент типа по-прежнему стираются.

inline fun Pair.asPairOf(): Pair? < if (first !is A || second !is B) return null return first as A to second as B >val somePair: Pair = "items" to listOf(1, 2, 3) val stringToSomething = somePair.asPairOf() val stringToInt = somePair.asPairOf() val stringToList = somePair.asPairOf>() val stringToStringList = somePair.asPairOf>() // Нарушает типобезопасность! fun main()

Непроверяемые (Unchecked) приведения

Как упоминалось выше, стирание типов делает невозможным проверку типа аргумента обобщения на этапе выполнения, и обобщенные типы в коде могут быть недостаточно связаны друг с другом, чтобы компилятор обеспечил типобезопасность.

Тем не менее иногда мы имеем программную логику высокого уровня, которая подразумевает типобезопасность.

fun readDictionary(file: File): Map = file.inputStream().use < TODO("Прочитать сопоставление строк с произвольными элементами.") >// Мы сохранили словарь (map) `Int`ов в файл val intsFile = File("ints.dictionary") // Warning: Unchecked cast: `Map` to `Map` val intsDictionary: Map = readDictionary(intsFile) as Map

Компилятор выдает предупреждение для приведения в последней строке. Приведение не может быть полностью проверено во время выполнения и нет дает гарантии, что значения в словаре (map) являются Int .

` and `DictionaryWriter ` interfaces with type-safe implementations for different types. You can introduce reasonable abstractions to move unchecked casts from the call site to the implementation details. Proper use of [generic variance](generics.md#variance) can also help. -->

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

Для обобщенных функций, используемых встроенные (inline) функции с параметрами вещественного типа приведение типа arg as T является проверяемым, до тех пор, пока тип arg не имеет свои аргументы типа, которые были стерты.

Предупреждение о непроверяемом приведении можно убрать используя аннотации @Suppress("UNCHECKED_CAST") .

inline fun List.asListOfType(): List? = if (all < it is T >) @Suppress("UNCHECKED_CAST") this as List else null 

В JVM, массивы ( Array ) сохраняют информацию о стираемом типе их элементов, и приведение типов к массиву частично проверяется: nullability и фактические аргументы для параметризированных элементов массива все еще стираются. Например, приведение foo as Array ?> будет успешным, если foo является массивом List , независимо от того, является ли он nullable или нет.

© 2015—2023 Open Source Community

Как узнать тип переменной kotlin

На этом шаге мы рассмотрим особенность автоматического определения типов переменных .

Обратите внимание, что типы, которые мы указали для переменных experiencePoints и playerName , выделены серым цветом в IntelliJ . Элементы, выделенные серым цветом, необязательны. Наведите указатель мыши на определение типа String , и IntelliJ объяснит, почему эти элементы необязательны (рисунок 1).

Рис.1. Избыточная информация о типе

Как видите, Kotlin определил, что ваше объявление типа "избыточно". Что это значит?

Kotlin поддерживает автоматическое определение типов , что позволяет опустить типы для переменных, которым присваиваются значения при объявлении. Так как при объявлении переменной playerName присваивается значение типа String и переменной experiencePoints присваивается значение типа Int , компилятор Kotlin автоматически определяет тип каждой переменной.

Так же как IntelliJ помогает поменять var на val , она может помочь убрать ненужное объявление типа. Щелкните на объявлении типа String (: String) рядом с playerName и выберите Remove exclicit type specification или нажмите комбинацию клавиш Alt + Shift + Enter (рисунок 1). :String исчезнет. Повторите процесс для experiencePoints var , чтобы убрать :Int .

Вне зависимости от того, используете вы возможность автоматического определения типов или указываете тип в объявлении каждой переменной, компилятор ведет учет типов. В дальнейшем мы будем использовать возможность автоматического определения типов, если это не создает двусмысленности. Это помогает придать коду аккуратный вид и упрощает его изменение в будущем.

Обратите внимание, что IntelliJ покажет вам тип любой переменой по вашему запросу, даже если ее тип не был объявлен явно. Чтобы узнать тип переменной, задержите курсор мыши на переменной, или щелкните на ее имени и нажмите Ctrl + Shift + P . IntelliJ покажет ее тип (рисунок 2).

Рис.2. Вывод информации о типе

На следующем шаге мы рассмотрим константы времени компиляции .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *