Як я можу перевірити загальний тип у Котліні?


84

Я намагаюся провести тест на загальний тип у Котліні.

if (value is Map<String, Any>) { ... }

Але компілятор скаржиться на

Не вдається перевірити, наприклад, стираний тип: jet.Map

Чек із звичайним типом працює добре.

if (value is String) { ... }

Використовується Kotlin 0.4.68.

Чого мені тут не вистачає?

Відповіді:


92

Проблема полягає в тому, що аргументи типу стираються, тому ви не можете перевірити повний тип Map, оскільки під час виконання немає інформації про ці String та Any.

Щоб обійти це, використовуйте символи підстановки:

if (value is Map<*, *>) {...}

Чудово! Це чудово працює! Я щойно збентежився прикладом у документації: confluence.jetbrains.net/display/Kotlin/Type+casts
Філіп Брюль,

51
Що робити, якщо ви насправді хочете перевірити, чи є щось, Collection<String>щоб зробити його автоматичним перекладом?
беруїчний

У мене такий фрагмент if (it.getSerializable(ARG_PARAMS) is HashMap<*, *>) {it.getSerializable(ARG_PARAMS) as HashMap<String, String>} else null. Так що в основному це збирається спробувати гіпсі , HashMap<String, Integer>щоб , HashMap<String, String>якщо я перевіряю проти загального типу. Мені чогось не вистачає?
Фарід

@FARID Так, так буде, і цей акторський склад не є безпечним
Андрій Бреслав

18

JVM видаляє загальну інформацію про тип. Але Котлін переосмислив дженерики. Якщо у вас є загальний тип T, ви можете позначити параметр типу T вбудованої функції як реіфікований, щоб він міг перевірити його під час виконання.

Отже, ви можете зробити:

inline fun <reified T> checkType(obj: Object, contract: T) {
  if (obj is T) {
    // object implements the contract type T
  }
}

3
Не могли б ви показати приклад, як телефонувати checkType()? Я не впевнений, що подати для другого аргументу.
Майкл Ософський,

10

Я думаю, що це більш доречний спосіб

inline fun <reified T> tryCast(instance: Any?, block: T.() -> Unit) {
    if (instance is T) {
        block(instance)
    }
}

Використання

// myVar is nullable
tryCast<MyType>(myVar) {
    // todo with this e.g.
    this.canDoSomething()
}

Ще один коротший підхід

inline fun <reified T> Any?.tryCast(block: T.() -> Unit) {
    if (this is T) {
        block()
    }
}

Використання

// myVar is nullable
myVar.tryCast<MyType> {
    // todo with this e.g.
    this.canDoSomething()
}

1
Чому щось подібне недоступне безпосередньо в kotlin stdlib :-(
ATom

Хіба не something as? Stringте саме? Зверніть увагу на знак питання після as?
Далібор Філус

@DaliborFilus ні. Йдеться про дженерики та стирані типи під час виконання. Якщо вам не доводиться мати справу з дженериками, ви можете просто скористатися as?, виправте.
stk

0

Я спробував наведене вище рішення, tryCast<Array<String?>>і, мабуть, у своєму конкретному завданні в списку з багатьма залученими кастингами це була не така чудова ідея, оскільки це різко сповільнило роботу.

Це рішення, яке я нарешті зробив - перевірити вручну записи та методи виклику, наприклад:

 fun foo() {
    val map: Map<String?, Any?> = mapOf()
    map.forEach { entry ->
        when (entry.value) {
            is String -> {
                doSomeWork(entry.key, entry.value as String)
            }
            is Array<*> -> {
                doSomeWork(entry.key, (entry.value as? Array<*>)?.map {
                    if (it is String) {
                        it
                    } else null
                }?.toList())
            }
        }
    }
}


private fun doSomeWork(key: String?, value: String) {

}
private fun doSomeWork(key: String?, values: List<String?>?) {

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.