当前位置: 首页 > 知识库问答 >
问题:

递归筛选和映射属性列表

茅炯
2023-03-14

我使用静态编程语言反射来检查具有特定注释的属性是否为空。

给出以下示例:

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

这个想法是,函数将迭代我的类的属性,检查它们是否有注释,并检查值是否为空。如果属性是一个数据类,代码会递归地计算Childs的属性。

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

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

如果不进行筛选,而是运行forEach并打印结果,则会得到预期的属性。所以我很确定这和过滤器内部的循环有关。

你认为有没有更实用的方法?我只是担心我不需要一个“临时”列表,向列表中添加值,然后重置它。

共有1个答案

钮出野
2023-03-14

您的函数递归地调用自身,但不处理该递归调用的返回列表。这就是为什么你只会得到顶级课程的成绩。

另外,在我看来,你不应该依赖于过滤器调用产生的副作用。它可能会工作,但函数的文档并不能保证集合中的每个项都会准确调用它一次。因此,应该有一个单独的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."
 类似资料:
  • 使用Java8 streams,我希望将列表转换为映射,如Java8 list into map 解决方案中所述。但是,我希望过滤以删除具有某些键的条目(例如,如果键为空),而不需要将值转换为键两次。

  • 如何过滤

  • 我想通过作为字符串的属性名筛选类的集合。假设我有一个名为Person的类,我有它的一个集合,或者是IEnumerable或者是List,我想过滤这个集合,但是我不知道确切的过滤器,我的意思是我不能使用: 让我举一个例子。

  • 我在做一些类似递归获取属性的事情 代码的问题是:它只下降了一级,我想知道如何使用反射自动获取所有属性?我刚刚编写了以下示例容器代码: 在主要方法中: 我希望得到: 我现在得到的结果是: 有人能帮我使用PrintProperties方法吗?非常感谢你。

  • 我对此代码的唯一关注是,它需要一个新表来连接和,我希望避免这种行为(但如果确实不可能的话,就继续执行它)。 使用也是不可行的,因为它需要表中的其他列,而且这个表稍后将被许多其他实体使用。 因此,使用数据示例,如果在我的表中有一个have: 在Java中,我的列表应该有两个条目(可用的两种语言)。 这种行为在JPA中可能吗? 解决方案 现在一切都按预期工作。