Что такое smart cast?
В Kotlin smart cast (умное приведение типов) — это механизм, при котором компилятор автоматически понимает, что переменная уже приведена к нужному типу после соответствующей проверки, и не требует явного кастинга (приведения типа) с помощью оператора as.
Основная идея
Smart cast избавляет от необходимости вручную писать:
if (x is String) {
val length = (x as String).length
}
Вместо этого можно просто написать:
if (x is String) {
val length = x.length // smart cast работает здесь
}
Компилятор "видит", что внутри if (x is String) переменная x уже гарантированно String, и позволяет обращаться к её членам, как к объекту этого типа.
Когда работает smart cast
1. Типовая проверка с is
fun printLength(obj: Any) {
if (obj is String) {
println(obj.length) // smart cast: obj → String
}
}
2. Проверка != null
fun printText(s: String?) {
if (s != null) {
println(s.length) // smart cast: s → String (а не String?)
}
}
3. Проверка через else или when
fun test(x: Any) {
when (x) {
is Int -> println(x + 1)
is String -> println(x.uppercase())
}
}
4. Условие x is Type && …
if (x is List<\*> && x.isNotEmpty()) {
println(x\[0\]) // smart cast: x → List<\*>
}
Когда smart cast не срабатывает
Smart cast не работает, если переменная изменяемая (var) и доступна вне текущего потока исполнения, потому что её значение может измениться в любой момент — и компилятор не может гарантировать безопасность.
Пример: не работает с var
var x: Any = "text"
if (x is String) {
println(x.length) // ❌ Ошибка: Smart cast невозможен
}
Решение — использовать val:
val x: Any = "text"
if (x is String) {
println(x.length) // ✅ OK
}
Пример: с открытым свойством
open class A {
open val x: Any = "string"
}
fun check(a: A) {
if (a.x is String) {
println(a.x.length) // ❌ Smart cast невозможен
}
}
Поскольку свойство x открыто и может быть переопределено в потомках, его тип не гарантирован на этапе компиляции. Рекомендуется сохранять значение во временную переменную:
val value = a.x
if (value is String) {
println(value.length) // ✅ Работает
}
Примеры с val, var, полями
val локальная переменная — ✅ OK
val x: Any = "hello"
if (x is String) println(x.length)
val — свойство объекта (не open) — ✅ OK
class MyClass(val x: Any)
fun printLength(obj: MyClass) {
if (obj.x is String) {
println(obj.x.length) // Smart cast работает
}
}
var — локальная переменная — ❌ Smart cast НЕ сработает
var y: Any = 42
if (y is Int) {
println(y + 1) // Ошибка: smart cast невозможен
}
Smart cast в when
Механизм отлично работает в when, особенно без использования else, так как Kotlin умеет анализировать ветки типов:
fun process(x: Any) {
when (x) {
is Int -> println("Int: ${x + 1}")
is String -> println("String: ${x.length}")
else -> println("Unknown")
}
}
Сложные логические выражения
Smart cast также работает при объединённых условиях, если компилятор может проследить их выполнение:
fun handle(x: Any?) {
if (x != null && x is String) {
println(x.uppercase()) // x → non-null String
}
}
``
### **Smart cast и return/throw**
Если переменная проверена после return, throw, или continue, компилятор тоже может применить smart cast:
```python
fun test(x: String?) {
if (x == null) return
println(x.length) // smart cast: x → String
}
Реализация и анализ
Smart cast работает только при контролируемом потоке исполнения, если компилятор может доказать, что тип не изменится. Он использует Data Flow Analysis (DFA), чтобы отслеживать значения переменных внутри области видимости. При сомнениях — лучше использовать временные переменные и явные приведения типа.
Полезно в:
-
Обработке nullable-типов
-
Проверках типа с is
-
when выражениях
-
Условных проверках без лишних as
Smart cast делает код более чистым, безопасным и выразительным без лишних ручных преобразований типов.