Kotlin:使用(输出)反射比较不同目标对象的属性值

joe*_*cks 5 reflection lambda kotlin

我想比较一个数据类的多个实例之间的值,以便我知道哪个值发生了变化:

data class A(val name : String)
val firstA = A("hello")
val secondA = A("you")

if (secondA.name.changed(firstA)) {
   // Do something
}
Run Code Online (Sandbox Code Playgroud)

我可以以某种方式访问​​的属性函数.name并在另一个目标值上执行它(在这个例子中是 firstA)而不显式定义它吗?代表可以在这里提供帮助,或者我如何在有和没有反思的情况下解决这个问题?

jrt*_*ell 7

使用反射

使用 Kotlin 反射库,您可以使用memberProperties和 按不同值进行过滤

import java.util.*
import kotlin.reflect.full.memberProperties

data class Example(val a: Int, val b:Long, val c: String)

fun main(args: Array<String>) {
    val start = Example(1,2, "A")
    val end = Example(1,4, "B")
    val differentFields = Example::class.memberProperties.filter {
        val startValue = it.get(start)
        val endValue = it.get(end)
        !Objects.equals(startValue, endValue)
    }

    differentFields.forEach {
        println(it.name)
    }
}
Run Code Online (Sandbox Code Playgroud)

输出

b
c
Run Code Online (Sandbox Code Playgroud)

不使用反射

您需要显式地检查每个方法(或将它们存储在列表中并迭代它们)

无列表

import java.util.*

data class Example(val a: Int, val b:Long, val c: String) {
    fun getChanged(other: Example): List<String> {
        val ret: MutableList<String> = mutableListOf()

        if (!Objects.equals(a, other.a)) ret.add("a")
        if (!Objects.equals(b, other.b)) ret.add("b")
        if (!Objects.equals(c, other.c)) ret.add("c")

        return ret
    }
}

fun main(args: Array<String>) {
    val start = Example(1,2, "A")
    val end = Example(1,4, "B")
    println(start.getChanged(end))
}
Run Code Online (Sandbox Code Playgroud)

带列表

import java.util.*

data class Example(val a: Int, val b:Long, val c: String) {

    data class Field(val function: (Example) -> Any?, val name: String)
    val fields: List<Field> = listOf(
            Field({ it.a }, "a"),
            Field({ it.b }, "b"),
            Field({ it.c }, "c")
    )

    fun getChanged(other: Example): List<String> {
        return fields.filter {
            !Objects.equals(it.function(this), it.function(other))
        }.map { it.name }
    }
}

fun main(args: Array<String>) {
    val start = Example(1,2, "A")
    val end = Example(1,4, "B")
    println(start.getChanged(end))
}
Run Code Online (Sandbox Code Playgroud)


joe*_*cks 6

没有反射

我找到了一种不使用反射的方法:

interface DiffValue<T> {
    val old : T?
}

fun <T,R> T.changed(memberAccess : T.() -> R) : Boolean {
    if (this is DiffValue<*>) {
        val old = this.old as? T
        if (old != null) {
            val currentValue = with(this, memberAccess)
            val previousValue = with(old, memberAccess)
            return currentValue != previousValue
        }
    }
    return true
}
Run Code Online (Sandbox Code Playgroud)

这就是你如何使用它:

data class A(val val name: String, override val old : A? = null)
val firstA = A("hello")
val secondA = A("you", firstA)

if (secondA.changed {name}) {
   // Do something
}
Run Code Online (Sandbox Code Playgroud)

这是基于带有接收器lambda 文字的概念,它允许 Kotlin 构建强大的构建器和 DSL。

注意:该DiffValue接口是可选的,仅用于使将值保持在一起更容易一些并避免在changed.