【问题标题】:Recursively filter and map a list of properties递归过滤和映射属性列表
【发布时间】:2020-01-16 05:08:38
【问题描述】:

我正在使用 Kotlin 反射来检查具有特定注释的属性是否为空。

举个例子:

data class DataClass(
    @SomeRandomAnnotation
    val otherAnnotated: String?,
    val inner: InnerClass
)

data class AnotherDataClass(
    @SomeRandomAnnotation
    val annotatedProperty: String?,
    val dataClass: DataClass
) {

    fun checkCreditAnalysisConstrain() {
        print(checkConstrain(this))
    }
}

以及检查它的函数:

fun checkConstrain(parentClass: Any): List<String> {
    val filter = parentClass::class.memberProperties.filter {
        if (memberIsDataClass(it)) checkConstrain(getMemberPropertyInstance(parentClass, it))

        hasAnnotation(it) && propertyIsNull(it, parentClass)
    }
    return filter.map { formatResult(parentClass, it) }
}

这个想法是,该函数将遍历我的类的属性,检查它们是否具有注释并检查值是否为空。 如果属性是一个数据类,代码会递归地评估孩子的属性。

之后,我映射结果,将 KProperty 转换为人类可读的简单字符串,包含类名和属性名。

问题是上面的代码没有按预期工作。返回的属性只是一级类的属性。

如果我不做过滤器,而是运行 forEach 并打印结果,我会得到预期的属性。所以我很确定这与过滤器内的重复出现有关。

您是否发现了任何以更实用的方式执行此操作的方法?我只是担心我不需要“临时”列表并将值添加到列表中并在之后重置它。

【问题讨论】:

    标签: algorithm kotlin reflection


    【解决方案1】:

    您的函数递归调用自身,但对返回的递归调用列表不做任何事情。这就是为什么您只能获得顶级课程的结果。

    另外,在我看来,您不应该依赖 filter 调用所产生的副作用。它可能有效,但该函数的文档并不能保证集合中的每个项目都会调用一次。所以应该有一个单独的for循环来进行递归调用,并且应该将结果添加到现有结果中。

    fun checkConstrain(parent: Any): List<String> {
        val memberProperties = parent::class.memberProperties
        var result = memberProperties
            .filter { hasAnnotation(it) && propertyIsNull(it, parent) }
            .map { formatResult(parent, it) }
        memberProperties.filter { memberIsDataClass(it) }
            .mapNotNull { getMemberPropertyInstance(parent, it) }
            .forEach { result += checkConstrain(it) }
        return result
    }
    

    您没有为您使用的几个函数提供代码。这是我为他们使用的:

    val KProperty<*>.returnTypeClass get() = this.returnType.classifier as? KClass<*>
    fun <T> memberIsDataClass(member: KProperty<T>) = member.returnTypeClass?.isData == true
    fun <T> getMemberPropertyInstance(parent: Any, property: KProperty<T>) = property.getter.call(parent)
    fun <T> hasAnnotation(property: KProperty<T>) = property.annotations.firstOrNull { it.annotationClass == SomeRandomAnnotation::class } != null
    fun <T> propertyIsNull(property: KProperty<T>, parent: Any) = getMemberPropertyInstance(parent, property) == null
    fun formatResult(parent: Any, property: KProperty<*>) = "$parent's property(${property.name}) is annotated with SomeRandomAnnotation and is null."
    

    【讨论】:

      猜你喜欢
      • 2023-02-22
      • 1970-01-01
      • 2021-04-19
      • 2019-08-20
      • 1970-01-01
      • 2018-04-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多