KProperty1.getDelegate 上的泛型过于严格?

标签 generics reflection kotlin

我正在尝试获取类层次结构中某种类型的所有属性委托(delegate)。这给我带来了一些问题。
首先,似乎没有直接的方法可以在 Kotlin 中获得类的父类(super class)。我尝试的第一种方法是使用 Java 的 Class.getSuperclass :

private fun <T : Any> KClass<T>.getSuperclass(): KClass<in T>? = (java.superclass as? Class<out Any>)?.kotlin as KClass<in T>

但这需要未经检查的强制转换,因为 Kotlin 不允许我转换 Class<in T>回到 Kotlin,因为显然在 T 中意味着它可能不是 Any,无论它可能是什么(“类型参数有一个上限 'Any' 不能满足捕获 'in' 投影”)。

所以我尝试了一个纯 Kotlin 解决方案:首先获取所有父类(super class),包括接口(interface) (KClass.superclasses),然后过滤掉每个接口(interface)。但是 KClass 上什么都没有这告诉我它是否是一个接口(interface)!所以我不得不再次去Java。它再次需要未经检查的类型转换,因为出于某种原因KClass.superclassesList<KClass<*>> 类型而不是 List<KClass<in T>> :
private fun <T : Any> KClass<T>.getSuperclass123(): KClass<in T>? = superclasses.firstOrNull { it.java.isInterface } as KClass<in T>?

我在这里错过了什么吗?我必须。

现在对于实际问题,尝试获取类层次结构中的所有属性委托(delegate)实例。首先,我编写了一个函数来获取一个类的实例:
private fun <T : Any> KClass<T>.getProperties(obj: T) =
    declaredMemberProperties.also { it.forEach { it.isAccessible = true } }.asSequence().filter { it.getDelegate(obj) is MyPropertyDelegate }

工作正常。但当然,我还需要在父类(super class)中声明的所有属性。所以很自然地我尝试将接收器更改为KClass<in T>这当然会导致我无法调用 declaredMemberProperties了。我该如何解决这个问题?

我已经尝试过使用 KClass.memberProperties ,但是如果子类覆盖了在父类中委托(delegate)的属性,则它就不足了。在这种情况下,它只会列出被覆盖的属性,这不允许我访问属性委托(delegate)实例。

编辑:
我做了一些更多的测试,下面的 Java 代码编译得很好,正如我所期望的:
static <T> List<MyProperty> getProperties(KClass<? super T> clazz, T obj) {
    return KClasses.getDeclaredMemberProperties(clazz).stream()
            .map(p -> p.getDelegate(obj))
            .filter(MyProperty.class::isInstance)
            .map(MyProperty.class::cast)
            .collect(Collectors.toList());
}

等效的 Kotlin 代码是什么?

编辑²:
超一流的东西也是如此。以下(尽管仍然是丑陋的解决方法)在 Java 中编译,但在 Kotlin 中不编译(此处省略 null-check):
static <T> KClass<? super T> getSuperClass(KClass<T> clazz) {
    return JvmClassMappingKt.getKotlinClass(JvmClassMappingKt.getJavaClass(clazz).getSuperclass());
}

最佳答案

在 kotlin 你的 getProperties()可能看起来像这样:

inline fun <reified T:Any> T.getProperties(): List<MyProperty> = T::class.declaredMemberProperties.asSequence()
        .map { it.getDelegate(this) }
        .filter(MyProperty::class::isInstance)
        .map(MyProperty::class::safeCast)
        .filterNotNull() 
        .distinct()
        .toList()

并且 kotlin 确实没有内置支持检查 KClass代表一个接口(interface),这真是一件尴尬的事情。无论如何,我们有一个解决方法:
val KClass<*>.isInterface: Boolean
    get() = constructors.isEmpty()

这是任意 KClass 的扩展属性,测试它是否代表一个接口(interface)。这是基于类,无论是抽象类、开放类、内部类还是最终类,都必须至少有一个构造函数,而接口(interface)根本不能有任何构造函数。但是,过滤掉接口(interface)并不是一个好主意,因为在 kotlin 中,interface can also have properties .

结合以上所有想法,我们得出这样的结果:
inline fun <reified T : Any> T.getProperties(): List<MyProperty> = T::class.allSuperclasses.asSequence()
        .flatMap { it.declaredMemberProperties.asSequence() }
        .map { @Suppress("UNCHECKED_CAST") (it as KProperty1<in T, *>).getDelegate(this) }
        .filter(MyProperty::class::isInstance)
        .map(MyProperty::class::safeCast)
        .filterNotNull()
        .distinct()
        .toList()

奇怪的未经检查的类型转换是 allClasses 的解决方法。宣言。我没有过多地研究它的实现,所以我不知道这是技术上的限制还是疲惫的程序员的懒惰,但是那个星形投影迫使我使用未经检查的类型转换。我们都知道这将是安全的。

关于KProperty1.getDelegate 上的泛型过于严格?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43569835/

相关文章:

java - 如何确定 Java 字段是否具有 transient 修饰符?

使用 fragment : Adapter not receiving callback 的 Android Jetpack 分页

java - 在 Android Studio 中将代码从 Java 转换为 Kotlin

C# - 使用通用枚举(或控制列表的替代方法)

java - Iterator<T> 返回对象

java - 在java中使用泛型编写方法时<E>到底做了什么?

java - 如何避免 OSGi 中的 class.forName()?

java - 接受两种不同类型作为参数的方法

javascript - 无法枚举 getter/setter 属性

android - 如何使用 Kotlin 创建自定义 View 的构造函数