Avatar ·

Как проверить тип объекта (instanceof) в Kotlin? Аналог Java instanceof

Привет! Я только начал изучать Kotlin после Java и столкнулся с проблемой. В Java я постоянно использовал оператор instanceof, чтобы проверить, является ли объект экземпляром определённого класса. Например:

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

Но в Kotlin я не могу найти прямой аналог. Пытался написать obj instanceof String, но компилятор выдаёт ошибку. Пробовал использовать is — вроде бы сработало, но не уверен, правильно ли это.

Вот что я написал:

fun checkType(obj: Any) {
    // Пытаюсь проверить тип
    if (obj is String) {
        // Хочу вызвать метод length()
        println(obj.length)
    }
}

Этот код скомпилировался и работает. Но у меня несколько вопросов:

  • Правильно ли я использую is вместо instanceof?
  • Нужно ли после проверки типа делать явное приведение (как (String) obj в Java)?
  • Есть ли ещё какие-то способы проверки типа в Kotlin?
  • Как быть с проверкой на null?

Буду благодарен за подробное объяснение! Хочется понять идиоматический подход в Kotlin.

<<>>

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

Да, вы всё делаете правильно! В Kotlin для проверки типа объекта используется оператор is. Это полный аналог Java-оператора instanceof, но с одним важным преимуществом — умным приведением типов (smart cast).

Базовое использование

val obj: Any = "Hello, Kotlin!"

if (obj is String) {
    // Smart cast: obj автоматически приведён к String
    println(obj.length)  // Работает без явного приведения
    println(obj.uppercase())
}

В Kotlin не нужно явно приводить тип после проверки с помощью is. Компилятор сам анализирует ветки кода и автоматически приводит объект к нужному типу. Это называется smart cast.

Обратная проверка: !is

if (obj !is String) {
    println("Объект не является строкой")
    // Здесь obj всё ещё Any
} else {
    // Здесь obj автоматически стал String
    println(obj.length)
}

Проверка на null

Для проверки на null используйте те же операторы:

val nullableStr: String? = "Kotlin"

if (nullableStr is String) {
    // Smart cast сработает, но nullableStr всё ещё может быть null?
    // Нет! is уже проверил, что объект не null
    println(nullableStr.length)  // Безопасно
}

// Или более кратко:
if (nullableStr != null) {
    println(nullableStr.length)  // Smart cast от String? к String
}

Когда smart cast НЕ работает

Умное приведение не сработает, если:

  • Тип проверяется на var (изменяемая переменная) — компилятор не может гарантировать, что значение не изменилось между проверкой и использованием
  • Объект может быть изменён другим потоком
var obj: Any = "Test"
if (obj is String) {
    // Ошибка компиляции! Smart cast не работает для var
    // println(obj.length)  // Не скомпилируется
    // Решение: используйте val или явное приведение
    val str = obj as String
    println(str.length)
}

Явное приведение: оператор as

Если вы уверены в типе, можно использовать as (аналог (Type) obj в Java):

val obj: Any = "Kotlin"
val str: String = obj as String  // Бросает ClassCastException, если тип не совпадает

// Безопасное приведение: as?
val safeStr: String? = obj as? String  // Вернёт null, если тип не совпадает

Проверка нескольких типов с when

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

Резюме

  • is — прямой аналог instanceof
  • Smart cast автоматически приводит тип после проверки
  • Для nullable типов проверка на null работает через != null
  • Явное приведение — as (опасное) или as? (безопасное)
  • Для var smart cast не работает — используйте val или явное приведение

Ваш пример с is String абсолютно корректен и является идиоматическим способом в Kotlin. Продолжайте в том же духе!

<<>>

Альтернативный подход: использование when с проверкой типов

В дополнение к основному ответу, хочу показать более продвинутый способ — использование when в качестве выражения для проверки типов. Это особенно удобно, когда нужно обработать много разных типов в одном блоке:

fun process(obj: Any) {
    val result = when (obj) {
        is String -> {
            println("Обработка строки")
            obj.length  // Smart cast работает и здесь
        }
        is Int -> {
            println("Обработка числа")
            obj * 2
        }
        is Boolean -> {
            println("Обработка булева значения")
            if (obj) 1 else 0
        }
        else -> {
            println("Неизвестный тип")
            -1
        }
    }
    println("Результат: $result")
}

Комбинирование с проверкой на null

fun safeProcess(obj: Any?) {
    when (obj) {
        null -> println("Объект null")
        is String -> println("Строка: ${obj.length}")
        else -> println("Другой тип: ${obj::class.simpleName}")
    }
}

Использование is в фильтрации коллекций

val mixedList: List = listOf("Kotlin", 42, true, "Java", 3.14)

// Отфильтровать только строки
val strings: List = mixedList.filterIsInstance()
println(strings)  // [Kotlin, Java]

// Или вручную:
val strings2 = mixedList.filter { it is String }
    .map { it as String }  // Явное приведение, т.к. filter не меняет тип

Этот подход особенно полезен, когда вы работаете с иерархией классов или sealed классами — when становится незаменимым инструментом для типобезопасной обработки.