【问题标题】:How to iterate over nested data classes using kotlin and refelction如何使用 kotlin 和反射迭代嵌套数据类
【发布时间】:2020-11-28 04:17:33
【问题描述】:

我有一个名为 report 的数据类,它本质上是一个变量列表列表 - 因此是嵌套数据类。

data class report(
    var list1 : List1,
    var list2 : List2
) : Parcelable

data class List1 (
    var a: Boolean = false,
    var b: Boolean = false,
) : Parcelable 


data class List2 (
    var c : Boolean = false,
    var d : Boolean = false,
) : Parcelable 

使用 Kotlin 和反射,我有一个 for 循环,它遍历顶层数据类并成功打印变量的名称。我想遍历每个嵌套类并打印变量名称,但如果不按名称显式引用嵌套类,我无法弄清楚如何做到这一点。第一个 for 循环打印正确的数据,但第二个 for 循环只打印“长度”。

        for (prop in report::class.memberProperties) {
            Log.d("DATACLASS", "${prop.name}")
            for (subProp in prop::class.memberProperties){
                Log.d("DATACLASS", "${subProp.name}")
            }
        }

在第二个循环中,我觉得“prop”没有被正确扩展为对嵌套类的引用。我尝试了诸如“report.prop.name”或“report.${prop.name})”之类的方法,但没有成功。我还尝试将 report.prop.name 分配给一个单独的变量。这是我缺少的一个简单的语法问题吗?我确实在 Stack 上阅读了其他类似的问题,但没有找到解决方案。是否可能与没有在正确的时刻创建类实例有关?

【问题讨论】:

    标签: kotlin reflection


    【解决方案1】:

    问题在于您正在尝试迭代可能不同类型的属性 - 因此反射无法知道顶级之后的类型是什么。

    report::class.declaredMemberProperties 返回一个包含KMutableProperty1 对象的集合。您无法派生 KMutableProperty1 的泛型类型,因为泛型已被删除,因此可能是任何东西。当然知道它们是KMutableProperty1<record, List1>KMutableProperty1<record, List2>,但你是在编译时用泛型查看它。反射必须在运行时动态完成,没有泛型,因此在使用反射时,您只知道类型为KMutableProperty1

    我相信这意味着无法动态循环您描述的属性,因为属性类型在运行时是未知的。这样做的唯一方法是显式声明每个属性的类型,但这有点违背了这一点:

    fun <T : Any> KClass<T>.printPropertyNames(): Unit {
        for (prop in declaredMemberProperties) {
            println("$this: ${prop.name}")
        }
    }
    
    fun main() {
        Report::class.printPropertyNames()
        for (prop in Report::class.declaredMemberProperties) {
            when (prop.name) {
                "list1" -> List1::class.printPropertyNames()
                "list2" -> List2::class.printPropertyNames()
            }
        }
    }
    

    输出:

    class Report: list1
    class Report: list2
    class List1: a
    class List1: b
    class List2: c
    class List2: d
    

    如果您只想查看对象中的内容,可以使用数据类的默认 toString() 行为:

    println(Report(List1(true, false), List2(false, true)))
    

    输出:

    Report(list1=List1(a=true, b=false), list2=List2(c=false, d=true))
    

    【讨论】:

    • 谢谢亚当。这是否解释了为什么我不能使用抽象函数引用嵌套类?比如:println (report.prop.name)
    • 问题是propKMutableProperty1,所以subProp代表KMultableProperty1类的属性,而不是List1List2等的属性。
    【解决方案2】:

    所以我设法用一个函数和一个 for 循环来做到这一点,如下所示:

    fun <T> printProperty(instance: T, prop: KProperty1<T, *>): Any? {
            println("${prop.name} = ${prop.get(instance)}")
            return prop.get(instance)
        }
    
    for (rep in MyReport::class.memberProperties) {
            val reportProperty = MyReport::class.memberProperties.single{ it.name == rep.name}
            val reportInstance = printProperty(report, reportProperty)
            for (subRep in reportInstance!!::class.memberProperties) {
                println("${subRep.name} = ${subRep.getter.call(reportInstance)}")
                }
            }
    

    我不知道为什么,但我不能在第二个 for 循环中再次使用我的函数 - 相反,我必须使用 getter.call 来获得我想要的值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-10
      • 2017-04-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多