我需要在Kotlin的运行时动态加载类。我想检查一下它们是否实现了我的界面,如果是,则全部变为绿色。不幸的是,科特林的“智能演员”让我失望了:
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>
我正在清楚地检查该类是否实现了给定的接口。我可以重新编写此代码以避免警告吗?
我尝试用is KClass<MyInterface>
进行测试,但遇到类型擦除错误(显然,因为通用类型信息在运行时消失了)。
编辑:为澄清起见,我的应用程序需要在配置期间在启动时读取类名"some.class.Name"
;加载这些类;检查它们是否满足接口;并存储Class或KClass引用以备后用。在运行时,它将使用cls.createInstance()
之类的那些引用来创建对象。
我的问题:有没有办法得到不安全的转换警告?
当我将KClass<*>
强制转换为KClass<MyInterface>
时,我可以在配置时得到警告(即使我require
将该类设为子类),但随后却没有稍后会发出警告,因为.createInstance()
类引用上的KClass<MyInterface>
返回类型检查过的MyInterface
实例。
或者,我可以在没有配置警告的情况下将引用存储为KClass<*>
,但是在创建实例的地方我会得到警告,因为我需要不安全地投射{{ 1}}个实例到Object
。
有没有可以满足编译器要求的解决方案?
答案 0 :(得分:2)
JVM和Kotlin仅在编译器级别实现泛型。在运行时无法看到通用类的通用参数。 https://docs.oracle.com/javase/tutorial/java/generics/erasure.html
在运行时,Class<*>
和Class<MyInterface>
之间没有区别。这两个是Class
类型的相同实例。
您所拥有的警告意味着在运行时通用参数中没有信息,编译器也无法对其进行验证,并且它只能信任您
我看不到将KClass
强制转换为KClass<MyInterface>
的原因。它仅对于对象是必需的,而不是类。另外,使用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
答案 1 :(得分:1)
此类型转换不仅未经检查,而且实际上是不正确的:因为AMyInterfaceImpl::class
的类型为KClass<AMyInterfaceImpl>
,而KClass
的类型不是协变的(有充分的理由),不是的类型为KClass<MyInterface>
。您可以从以下代码中看到它没有编译:
class AMyInterfaceImpl : MyInterface { ... }
val cls: KClass<MyInterface> = AMyInterfaceImpl::class
因此,如果可以检查演员表,它将失败。
KClass<out MyInterface>
是正确的,但是我认为编译器不会理解这一点并允许智能转换。教编译器很少有用。