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

Операторы is и !is

Мы можем проверить принадлежит ли объект к какому-либо типу во время исполнения с помощью оператора is или его отрицания !is:

if (obj is String) {
    print(obj.length)
}

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

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

Во многих случаях, в 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 is автоматически преобразовывается в 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, код выше выбросит исключение. Чтобы соответствовать семантике приведений в Java, нам нужно указать nullable тип в правой части приведения:

val x: String? = y as String?

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

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

val x: String? = y as? String

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