假设我有一个类似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)
方法来复制除x
之外的所有字段,并使用x
覆盖x1
。例如,fun foo(a: A): A { a.copyX(100) }
上面的定义可能有用,但是在所有数据类上重复
copyX
似乎很笨拙。您如何建议摆脱这种重复的copyX
? 最佳答案
首先,您可以将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
中获取 copy
或名为KClass
的函数,然后填充调用的参数,按名称查找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-reflect
的依赖,并且仅适用于Kotlin/JVM。同样,反射也有一些性能开销,因此它不适用于对性能有严格要求的代码。您可以使用Java反射(this::class.java
,getMethod(...)
)代替(这会更冗长)并缓存反射实体来对此进行优化。