如何在 Kotlin 中安全地投射反射 class
How to safely cast a reflected class in Kotlin
我需要在 Kotlin 运行时动态加载 classes。我想检查他们是否实现了我的接口,如果是的话,全部都是绿色的。不幸的是,Kotlin 的 "smart casts" 让我失望了:
var className = "some.class.Name"
val unsafeClass = Class.forName(className).kotlin
require(unsafeClass.isSubclassOf(MyInterface::class)) {
"Class '$className' is not a MyInterface"
}
val safeClass = unsafeClass as KClass<MyInterface>
^^^^^^^^^^^^^^^^^^^^^^
Unchecked cast: KClass<out Any!> to KClass<MyInterface>
我正在清楚地检查 class 是否实现了给定的接口。我可以重新措辞此代码以避免警告吗?
我尝试使用 is KClass<MyInterface>
进行测试,但出现类型擦除错误(很明显,因为通用类型信息在运行时消失了。)
编辑:澄清一下,我的应用程序需要在启动时和配置期间读取 class 名称 "some.class.Name"
;加载那些 classes;检查它们是否满足接口;并存储一个 Class 或 KClass 参考以备后用。在运行时,它将使用这些引用来创建对象,使用 cls.createInstance()
或类似的。
我的问题:有什么方法可以在不收到不安全转换警告的情况下这样做吗?
我可以在配置时收到警告,当我将 KClass<*>
转换为 KClass<MyInterface>
时(即使我 require
d class 是一个子class) 但后来我没有收到任何警告,因为 .createInstance()
在 KClass<MyInterface>
class 上引用 returns 类型检查的 MyInterface
实例。
或者,我可以将引用存储为 KClass<*>
,而不会在配置时发出警告,但随后我会在创建实例的地方收到警告,因为我需要不安全的转换Object
个实例到 MyInterface
。
有没有满足编译器的解决方案?
JVM 和 Kotlin 仅在编译器级别实现泛型。在运行时看不到泛型 class 的泛型参数。
https://docs.oracle.com/javase/tutorial/java/generics/erasure.html
在运行时,Class<*>
和 Class<MyInterface>
没有区别。这两个是 Class
类型的相同实例。
你的警告意味着你在运行时没有通用参数中的信息,编译器也无法验证它,它只能信任你
我看不出将 KClass
转换为 KClass<MyInterface>
的原因。它只是一个对象所必需的,而不是class。此外,它可能可以简化为使用 Class<*>
代替,例如:
val className = "some.class.Name"
val unsafeClass = Class.forName(className)
require(MyInterface::class.java.isAssignableFrom(unsafeClass)) {
"Class '$className' is not a MyInterface"
}
val safe = unsafeClass.newInstance() as MyInterface
这个转换不仅未经检查,而且实际上是不正确的:因为 AMyInterfaceImpl::class
的类型是 KClass<AMyInterfaceImpl>
而 KClass
不是协变的(有充分的理由),它 not 具有类型 KClass<MyInterface>
。您可以从这段代码中看到未编译:
class AMyInterfaceImpl : MyInterface { ... }
val cls: KClass<MyInterface> = AMyInterfaceImpl::class
因此,如果可以检查演员表,它就会失败。
KClass<out MyInterface>
是正确的,但我认为编译器不会理解这一点并允许智能转换。教编译器的用处太少了。
我需要在 Kotlin 运行时动态加载 classes。我想检查他们是否实现了我的接口,如果是的话,全部都是绿色的。不幸的是,Kotlin 的 "smart casts" 让我失望了:
var className = "some.class.Name"
val unsafeClass = Class.forName(className).kotlin
require(unsafeClass.isSubclassOf(MyInterface::class)) {
"Class '$className' is not a MyInterface"
}
val safeClass = unsafeClass as KClass<MyInterface>
^^^^^^^^^^^^^^^^^^^^^^
Unchecked cast: KClass<out Any!> to KClass<MyInterface>
我正在清楚地检查 class 是否实现了给定的接口。我可以重新措辞此代码以避免警告吗?
我尝试使用 is KClass<MyInterface>
进行测试,但出现类型擦除错误(很明显,因为通用类型信息在运行时消失了。)
编辑:澄清一下,我的应用程序需要在启动时和配置期间读取 class 名称 "some.class.Name"
;加载那些 classes;检查它们是否满足接口;并存储一个 Class 或 KClass 参考以备后用。在运行时,它将使用这些引用来创建对象,使用 cls.createInstance()
或类似的。
我的问题:有什么方法可以在不收到不安全转换警告的情况下这样做吗?
我可以在配置时收到警告,当我将 KClass<*>
转换为 KClass<MyInterface>
时(即使我 require
d class 是一个子class) 但后来我没有收到任何警告,因为 .createInstance()
在 KClass<MyInterface>
class 上引用 returns 类型检查的 MyInterface
实例。
或者,我可以将引用存储为 KClass<*>
,而不会在配置时发出警告,但随后我会在创建实例的地方收到警告,因为我需要不安全的转换Object
个实例到 MyInterface
。
有没有满足编译器的解决方案?
JVM 和 Kotlin 仅在编译器级别实现泛型。在运行时看不到泛型 class 的泛型参数。 https://docs.oracle.com/javase/tutorial/java/generics/erasure.html
在运行时,Class<*>
和 Class<MyInterface>
没有区别。这两个是 Class
类型的相同实例。
你的警告意味着你在运行时没有通用参数中的信息,编译器也无法验证它,它只能信任你
我看不出将 KClass
转换为 KClass<MyInterface>
的原因。它只是一个对象所必需的,而不是class。此外,它可能可以简化为使用 Class<*>
代替,例如:
val className = "some.class.Name"
val unsafeClass = Class.forName(className)
require(MyInterface::class.java.isAssignableFrom(unsafeClass)) {
"Class '$className' is not a MyInterface"
}
val safe = unsafeClass.newInstance() as MyInterface
这个转换不仅未经检查,而且实际上是不正确的:因为 AMyInterfaceImpl::class
的类型是 KClass<AMyInterfaceImpl>
而 KClass
不是协变的(有充分的理由),它 not 具有类型 KClass<MyInterface>
。您可以从这段代码中看到未编译:
class AMyInterfaceImpl : MyInterface { ... }
val cls: KClass<MyInterface> = AMyInterfaceImpl::class
因此,如果可以检查演员表,它就会失败。
KClass<out MyInterface>
是正确的,但我认为编译器不会理解这一点并允许智能转换。教编译器的用处太少了。