我想检查该函数是否具有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
问题:
IA
接口和ActionEvent
Java类的错误?A
类引发错误?A
类型的参数?答案 0 :(得分:5)
问题是你要检查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.")
}
- 也没有编译错误。
您应该做些什么来检查参数类型是某个类还是某个接口,如下所示。
{{1}}
答案 1 :(得分:2)
首先,您使用的是错误的运算符。 is
检查某些事物的实例是否为给定类。你没有实例。您改为使用KType
来检查它是A
,IA
还是ActionEvent
的实例,而不是。{/ p >
因此,您需要使用不同的运算符,即检查它们是否相等或调用方法isAssignableFrom()
。然后你需要检查你正在比较的两件事是正确的数据类型并做你期望的事情。
在另一个答案中,@迈克尔说你可以将Type
和Class
视为平等,但并非总是如此;不那么简单。有时Type
是Class
,但有时它是ParameterizedType
,GenericArrayType
,TypeVariable
或WildcardType
,与equals无法比较。如果你有一个使用泛型的方法的参数,那么这种方法是错误的。
这是一个不支持泛型的版本,如果在参数中使用泛型,它们将不匹配。这也比较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>
的参数是否匹配{{1 (FAL !!!)。或者,如果您尝试比较数组类型,则再次匹配。
要修复此泛型问题,我们还需要删除您正在检查的A::class.defaultType
。我们需要一个辅助函数来做到这一点。
以下是从Klutter library复制的paramter.type
,并删除了一般内容以生成KType
。您将需要KClass
依赖项才能使用此代码。 您可以通过仅使用Java kotlin-reflect
而不是直接在任何地方使用kotlin-refect
来删除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.")
}
表达式和辅助函数:
when
然后fun assignCheck(ancestor: KClass<*>, checkType: KType): Boolean =
ancestor.java.isAssignableFrom(checkType.javaType.erasedType())
表达式更改为:
when
为了比较完整的泛型,我们需要将所有内容转换为我们可以比较但仍具有泛型的内容。最简单的方法是将所有内容都放入Java 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.")
}
,因为很难将所有内容都放入Type
。首先我们需要一个KType
类型的类,我们也会从Klutter library中窃取它:
TypeReference
现在使用这个快速扩展方法:
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
但是在这样做的时候,我们再次破坏了继承检查。我们并没有真正检查泛型的协方差(他们自己可以进行超类检查)。