7

假设我有这样的sealed class层次结构:

sealed class A {
    abstract val x: Int
    abstract fun copyX(x1: Int): A
}

data class A1(override val x: Int, val s1: String) : A() {
    override fun copyX(x1: Int): A {
        return this.copy(x = x1)
    }
}

data class A2(override val x: Int, val s2: String) : A() {
    override fun copyX(x1: Int): A {
        return this.copy(x = x1)
    }
}

所有数据类都有字段x,并且应该提供copyX(x1: Int)复制所有字段的方法,但xxx1. 例如,

fun foo(a: A): A { a.copyX(100) }

上面的定义可能有效,但copyX所有数据类的重复似乎非常笨拙。你会如何建议摆脱这种重复copyX

4

1 回答 1

5

首先,您可以实现copyX为扩展(甚至A是 的成员),以便将代码集中在一个地方,并至少避免在copyX密封类子类型中重复该函数:

sealed class A {
    abstract val x: Int
}

fun A.copyX(x1: Int): A = when (this) {
    is A1 -> copy(x = x1)
    is A2 -> copy(x = x1) 
}

data class A1(override val x: Int, val s1: String) : A()

data class A2(override val x: Int, val s2: String) : A()

如果你有很多密封的子类型并且它们都是data类或有一个copy函数,你也可以用反射一般地复制它们。为此,您需要从 获取primaryConstructor或 命名的函数copyKClass然后填充调用的x参数,按名称查找参数并x1为其放置值,然后放置从 等调用中获得的值component1()component2()保留默认值其他参数的值。它看起来像这样:

fun A.copyX(x1: Int): A {
    val copyFunction = this::class.memberFunctions.single { it.name == "copy" }
    val args = mapOf(
        copyFunction.instanceParameter!! to this,
        copyFunction.parameters.single { it.name == "x" } to x1
    )
    return copyFunction.callBy(args) as A
}

这是因为callBy允许省略可选参数。

请注意,它需要依赖于Kotlin/JVM,kotlin-reflect并且只能与 Kotlin/JVM 一起使用。此外,反射有一些性能开销,因此它不适合性能关键代码。您可以通过使用 Java 反射 ( this::class.java, getMethod(...)) 来优化这一点(这会更冗长)并缓存反射实体。

于 2019-08-27T11:01:07.293 回答