代码之家  ›  专栏  ›  技术社区  ›  Tobia

如何在Kotlin中安全地强制转换反射类

  •  0
  • Tobia  · 技术社区  · 5 年前

    我需要在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" 启动时,在配置期间;加载这些类;检查它们是否满足接口要求;并存储一个类或KClass引用以供以后使用。在运行时,它将使用这些引用创建对象,使用 cls.createInstance() 或诸如此类。

    我可以在配置时得到警告,当我抛出 KClass<*> KClass<MyInterface> (即使我 require d类是一个子类),但后来我没有收到任何警告,因为 .createInstance() KClass<MyInterface> 类引用返回选中的类型 MyInterface

    KClass<*&燃气轮机; ,在配置时没有警告,但是我会在创建实例的地方得到警告,因为我需要进行不安全的强制转换 Object MyInterface

    有什么解决方案可以满足编译器的要求吗?

    0 回复  |  直到 5 年前
        1
  •  1
  •   Alexey Romanov    5 年前

    这种类型转换不仅是未经检查的,实际上是不正确的:因为 AMyInterfaceImpl::class 有类型 KClass<AMyInterfaceImpl> KClass 不是协变的(有充分的理由),而是协变的 有这种类型吗 KClass<MyInterface>

    class AMyInterfaceImpl : MyInterface { ... }
    
    val cls: KClass<MyInterface> = AMyInterfaceImpl::class
    

    因此,如果可以检查铸造,它将失败。

    KClass<out MyInterface> 这是正确的,但我认为编译器不会理解这一点,也不会允许智能强制转换。教编译器太没用了。

        2
  •  3
  •   Eugene Petrenko    5 年前

    JVM和Kotlin只在编译器级别实现泛型。在运行时无法看到泛型类的泛型参数。 https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

    Class<*> Class<MyInterface> . 这两个是同一个例子 Class 类型。

    我看不出有什么理由这样做 KClass KClass<MyInterface> 类<*&燃气轮机;

    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