Несовместимые типы: A и kotlin.reflect.KType

Я хочу проверить, что функция имеет параметр типа A используйте следующий код:

 import kotlin.reflect.* import javafx.event.ActionEvent interface IA {} class A {} class B { fun test(a: A, ia: IA, event: ActionEvent) { println(a) println(ia) println(event) } } fun main(args: Array<String>) { for (function in B::class.declaredMemberFunctions) { for (parameter in function.parameters) { when (parameter.type) { is IA -> println("Has IA interface parameter.") is ActionEvent -> println("Has ActionEvent class parameter.") is A -> println("Has A class parameter.") // <---- compilation error in this line } } } } 

но когда я пытаюсь скомпилировать его, я вижу следующую ошибку:

 > Error:(20, 19) Incompatible types: A and kotlin.reflect.KType 

Вопросов:

  1. Почему компилятор не ActionEvent ошибку для интерфейса IA и класса ActionEvent Java?
  2. Почему компилятор вызывает ошибку для класса A ?
  3. Как проверить, что функция имеет аргумент типа A ?

Дело в том, что вы пытаетесь проверить, является ли KType A , что всегда false . И компилятор знает об этом и вызывает ошибку компиляции. Но IA – это интерфейс, который реализует KType возможно, также реализует этот интерфейс, так что ошибки компиляции не существует. ActionEvent – это открытый класс, поэтому подтип может реализовывать KType – без компиляции.

Что вы должны сделать, чтобы проверить, является ли тип параметра каким-то классом или каким-то интерфейсом, является следующее.

 when (parameter.type.javaType) { IA::class.javaClass -> println("Has IA interface parameter.") ActionEvent::class.javaClass -> println("Has ActionEvent class parameter.") A::class.javaClass -> println("Has A class parameter.") } 

Во-первых, вы используете неправильный оператор. is проверка, если экземпляр чего-либо является данным классом. У вас нет экземпляра. Вместо этого у вас есть KType который вы пытаетесь проверить, является ли это экземпляром класса A или IA или ActionEvent , это не так.

Поэтому вам нужно использовать другой оператор, который должен проверить, равны ли они, или вызвать метод isAssignableFrom() . И тогда вам нужно проверить, что две вещи, которые вы сравниваете, – это правильные типы данных и делать то, что вы ожидаете.

В другом ответе @Michael говорит, что вы можете просто относиться к Type и Class одинаково для равенства, что не всегда верно; не так просто. Иногда Type является Class но иногда это ParameterizedType , GenericArrayType , TypeVariable или WildcardType которые не сопоставимы с равными. Таким образом, этот подход ошибочен, если у вас когда-либо был параметр для метода, который использует генерики, которые он ломает.

Вот версия, которая не поддерживает generics в том случае, если в параметре используются дженерики, они не будут совпадать. Это также сравнивает KType с использованием равенства, что означает, что он не работает для унаследованных классов, соответствующих суперклассу или интерфейсу. Но самое простое:

  when (parameter.type) { IA::class.defaultType -> println("Has IA interface parameter.") ActionEvent::class.defaultType -> println("Has ActionEvent class parameter.") A::class.defaultType -> println("Has A class parameter.") } 

Это прерывается, если, например, если класс A имел общий параметр T поэтому вы хотели проверить параметр A<String> или A<Monkey> вы не будете сопоставлять A::class.defaultType (FALSE !!!). Или, если вы попытаетесь сравнить типы массивов, снова не будет соответствовать.

Чтобы исправить эту проблему с генериками, нам необходимо также удалить paramter.type вы проверяете. Для этого нам нужна вспомогательная функция.

Здесь вы копируете библиотеку Klutter, которая берет KType и стирает генерики для создания KClass . Для использования этого кода вам понадобится зависимость от отношения kotlin-reflect . Вы можете удалить зависимость kotlin-refect , только работая с Java Class и не используя KClass любом месте напрямую. Некоторый другой код придется изменить.

Со следующей функцией расширения:

 fun KType.erasedType(): KClass<*> { return this.javaType.erasedType().kotlin } @Suppress("UNCHECKED_CAST") fun Type.erasedType(): Class<*> { return when (this) { is Class<*> -> this as Class<Any> is ParameterizedType -> this.getRawType().erasedType() is GenericArrayType -> { // getting the array type is a bit trickier val elementType = this.getGenericComponentType().erasedType() val testArray = java.lang.reflect.Array.newInstance(elementType, 0) testArray.javaClass } is TypeVariable<*> -> { // not sure yet throw IllegalStateException("Not sure what to do here yet") } is WildcardType -> { this.getUpperBounds()[0].erasedType() } else -> throw IllegalStateException("Should not get here.") } } 

Теперь вы можете написать свой код проще:

  when (parameter.type.erasedType()) { IA::class-> println("Has IA interface parameter.") ActionEvent::class -> println("Has ActionEvent class parameter.") A::class -> println("Has A class parameter.") } 

Таким образом, дженерики игнорируются, и это сравнивает исходный стертый класс друг с другом; но опять же без наследования.

Чтобы поддерживать наследование, вы можете использовать эту версию, слегка измененную. Вам нужна другая форма выражения и вспомогательная функция:

 fun assignCheck(ancestor: KClass<*>, checkType: KType): Boolean = ancestor.java.isAssignableFrom(checkType.javaType.erasedType()) 

Затем выражение when изменилось на:

 when { assignCheck(IA::class, parameter.type) -> println("Has IA interface parameter.") assignCheck(ActionEvent::class, parameter.type) -> println("Has ActionEvent class parameter.") assignCheck(A::class, parameter.type) -> println("Has A class parameter.") } 

Дополнительный кредит, сравнивающий полные генерики:

Чтобы сравнить полные дженерики, нам нужно преобразовать все в нечто, что мы можем сравнить, все еще имеющее дженерики. Самое простое – получить все в Java- Type потому что сложнее всего сделать это в KType . Сначала нам нужен TypeReference типа TypeReference , мы также украдем это из библиотеки Klutter :

 abstract class TypeReference<T> protected constructor() { val type: Type by lazy { javaClass.getGenericSuperclass().let { superClass -> if (superClass is Class<*>) { throw IllegalArgumentException("Internal error: TypeReference constructed without actual type information") } (superClass as ParameterizedType).getActualTypeArguments()[0] } } } 

Теперь можно использовать быстрый способ расширения:

 inline fun <reified T: Any> ft(): Type = object:TypeReference<T>(){}.type 

И тогда наше, when выражение может быть более подробным с дженериками:

 for (parameter in function.parameters) { when (parameter.type.javaType) { ft<IA>() -> println("Has IA interface parameter.") ft<ActionEvent>() -> println("Has ActionEvent class parameter.") ft<A<String>>() -> println("Has A<String> class parameter.") // <---- compilation error in this line ft<A<Monkey>>() -> println("Has A<Monkey> class parameter.") // <---- compilation error in this line } } 

Но при этом мы снова нарушили проверку наследования. И мы действительно не проверяем ковариацию дженериков (они сами могли бы проверять суперклассы).

Двойной дополнительный кредит, как насчет наследования И дженериков?!?

Эм, это не так весело. Мне придется немного подумать об этом, возможно, добавить его позже в Клиттер. Это довольно сложно.

  • Сравнение ссылок и стоимости
  • почему kotlin использует === сравнить примитивный тип, равный друг другу, если они имеют одинаковое значение
  • Недостаток равенства Котлина
  • «Нормальный» Котлин и Котлин для андроида отличаются?
  • Kotlin: Kotlin-script (.kts) не может использовать обычный код?
  • почему SomeClass :: class - это KClass <SomeClass>, но this :: class - это KClass <out SomeClass>
  • Не удалось настроить обновление плагина kotlin?
  • JsonProperty не работает при включенном разрешении
  • модификатор lateinit не допускается на свойства примитивного типа в Котлине
  • Утечка памяти активности Kotlin
  • В Котлине ошибка Джексона по десериализации с классом данных
  • Interesting Posts

    Интервал Рабочего Планировщика называется слишком нерегулярным

    Как сравнить время простоя MotionEvent с часами, какова его временная база?

    Проекция звезды Котлина на контравариантные типы

    Какие пакеты / функции импортируются по умолчанию в Котлин?

    Какой предпочтительный синтаксис при использовании инъекции зависимостей на основе аннотаций в Котлин?

    Что такое синтаксис класса <? extends class_name> в kotlin?

    Смарт-литье и сравнение внутри Когда выражение после «есть» проверка типа

    Как я могу получить случайное число в Котлине?

    Несколько запросов retrofit2 с использованием Flowable в Котлине

    Как показать только день и месяц в диалоговом окне выбора даты в Kotlin (Android)?

    Элемент ItemViewModel TornadoFx имеет значение null

    Сложить список для сопряжения с назначением деструктуризации в котлин

    Могу ли я получить KFunction из переменной типа функции в Kotlin?

    Многострочный макет регулярного выражения

    Лямбда в выражении типа множественного типа

    Давайте будем гением компьютера.