«instanceof» в Kotlin: проверка типа с is и when (гид)

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

«instanceof» в Kotlin: почему это не Java, и как это делает код чище

Если вы пришли в Kotlin из мира Java, вы наверняка привыкли к ключевому слову instanceof. Оно помогает узнать, является ли объект экземпляром определённого класса. Но в Kotlin этого оператора нет. И это не баг, а фича.

Kotlin предлагает гораздо более элегантный, безопасный и лаконичный способ проверить тип объекта — оператор is. Он не только отвечает на вопрос «кто ты?», но и автоматически приводит объект к нужному типу. Это называется умным приведением (smart cast), и это одна из тех фишек, которые делают код на Kotlin таким приятным.

В этом руководстве мы разберём:

  • Как использовать оператор is вместо instanceof;
  • Что такое умное приведение и как оно работает;
  • Как красиво обрабатывать несколько типов с помощью when;
  • Чем отличается безопасное приведение as? от обычного as;
  • И, конечно, реальные примеры кода.

Поехали!

1. Оператор is — прямой аналог instanceof

В Java вы бы написали:

if (obj instanceof String) {
    String str = (String) obj;
    System.out.println(str.length());
}

В Kotlin то же самое выглядит так:

if (obj is String) {
    println(obj.length) // Умное приведение! obj уже String
}

Обратите внимание: после проверки obj is String вам не нужно явно приводить obj к типу String. Компилятор Kotlin сам знает, что внутри блока if переменная obj гарантированно является строкой. Это и есть умное приведение (smart cast).

Как это работает под капотом?

Компилятор анализирует поток выполнения. Если он видит проверку is и не находит возможности изменить тип переменной между проверкой и использованием (например, в многопоточном коде или при изменении var), он автоматически приводит тип. Для val это работает всегда, для var — только если переменная не может быть изменена между проверкой и использованием.

2. Отрицание: !is

Хотите проверить, что объект не относится к типу? Используйте !is:

if (obj !is String) {
    println("Это не строка")
} else {
    // Здесь obj автоматически стал String
    println("Длина строки: ${obj.length}")
}

Удобно и читаемо, правда?

3. Множественные проверки с when

А теперь представьте, что у вас есть объект, который может быть одним из нескольких типов. В Java вы бы написали цепочку if-else if с instanceof. В Kotlin есть более элегантный инструмент — конструкция when.

fun describe(obj: Any): String {
    return when (obj) {
        is String -> "Строка длиной ${obj.length}"
        is Int -> "Целое число: $obj"
        is List<*> -> "Список из ${obj.size} элементов"
        is Map<*, *> -> "Карта с ${obj.size} записями"
        else -> "Неизвестный тип"
    }
}

fun main() {
    println(describe("Привет"))   // Строка длиной 6
    println(describe(42))         // Целое число: 42
    println(describe(listOf(1,2)))// Список из 2 элементов
}

Обратите внимание: внутри каждой ветки when умное приведение тоже работает! В ветке is String переменная obj уже считается строкой, и вы можете вызывать obj.length без дополнительного приведения.

Это намного компактнее и безопаснее, чем классический switch или цепочка if.

4. Безопасное приведение: as?

Иногда вам нужно не просто проверить тип, а сразу получить значение определённого типа. В Java вы бы сделали (String) obj, но это может выбросить ClassCastException. В Kotlin есть два оператора приведения:

  • as — обычное приведение (выбросит исключение, если тип не совпадает);
  • as? — безопасное приведение (вернёт null, если тип не совпадает).

Пример:

val obj: Any = "Kotlin"

// Обычное приведение — рискованно
val str1: String = obj as String // Ок
// val num: Int = obj as Int     // ClassCastException!

// Безопасное приведение
val str2: String? = obj as? String // "Kotlin"
val num2: Int? = obj as? Int       // null (без исключения)
println(num2) // null

as? идеально подходит, когда вы не уверены в типе и хотите избежать краша приложения. В сочетании с элвис-оператором ?: это даёт очень чистый код:

val length = (obj as? String)?.length ?: 0
println(length) // 6, если obj — строка, иначе 0

5. Проверка типа с generics: осторожно, стирание!

Важный момент: в Kotlin, как и в Java, информация об обобщённых типах (generics) стирается во время выполнения. Это значит, что вы не можете напрямую проверить, является ли объект List<String> или List<Int>.

val list: Any = listOf("a", "b")

if (list is List<*>) { // Это работает — проверяем, что это список
    println("Это список")
}

// А вот так — НЕ РАБОТАЕТ:
// if (list is List) { // Ошибка компиляции!
//     println("Это список строк")
// }

Почему? Потому что во время выполнения стирается информация о типе элементов. Для обхода этого ограничения используйте inline-функции с reified типами. Но это уже тема для отдельной статьи.

6. Когда использовать is, а когда as?

Простое правило:

  • Используйте is, когда вам нужно проверить тип и, возможно, выполнить разные действия.
  • Используйте as?, когда вам нужно получить значение определённого типа, и вы готовы получить null в случае неудачи.
  • Избегайте as без крайней необходимости — это источник ClassCastException.

На практике 90% случаев покрываются связкой is + умное приведение. Безопасное приведение as?

Блоги

Рекомендаций по книгам