KProperty1.getDelegate上的泛型限制太多?

我试图获得一个类层次结构中的某个类型的所有属性代表。 这给我提出了一些问题。 首先,在Kotlin看来,没有一种简单的方法可以获得班级的超类。 我尝试的第一种方法是使用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的东西,不管可能是什么(“Type参数有一个上在'投影'中'任何'不能被满足'捕获')。

所以我尝试了一个纯粹的Kotlin解决方案:首先获取包括接口( KClass.superclasses )的所有超类,然后过滤出每个接口。 但是KClass上没有什么KClass告诉我它是否是一个接口! 所以我必须再次去Java。 而且它又需要一个未经检查的转换,因为由于某种原因, KClass.superclasses的类型是List<KClass<*>>而不是List<KClass<in T>>

 private fun <T : Any> KClass<T>.getSuperclass123(): KClass<in T>? = superclasses.firstOrNull { it.java.isInterface } as KClass<in T>? 

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

现在对于实际的问题,试图获得一个类层次结构中的所有属性委托实例。 首先我写了一个函数来获取一个类的实例:

 private fun <T : Any> KClass<T>.getProperties(obj: T) = declaredMemberProperties.also { it.forEach { it.isAccessible = true } }.asSequence().filter { it.getDelegate(obj) is MyPropertyDelegate } 

工作正常。 当然,我也需要在超类中声明的所有属性。 所以自然我试图改变接收器到KClass<in T>这当然导致我不能够调用declaredMemberProperties了。 我如何解决这个问题?

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

编辑:我做了一些更多的测试,下面的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代表一个接口的支持,这实在是一个尴尬的事情。 无论如何,我们有一个解决方法:

 val KClass<*>.isInterface: Boolean get() = constructors.isEmpty() 

这是任意KClass的扩展属性,测试它是否表示一个接口。 这是基于类是抽象的,开放的,内部的还是最终的,必须至少有一个构造函数,而接口根本不能有任何构造函数。 但是,过滤接口不是一个好主意,因为在kotlin中接口也可以有属性 。

结合以上所有的想法,我们得出这样的结果:

 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() 

werid unchecked cast是allClasses声明的解决方法。 我没有太多的研究它的实现,所以我不知道这是一个疲惫的程序员的技术约束还是懒惰,但是星形投影迫使我使用一个未经检查的投射。 我们都知道这将是安全的。