如何在这个密封的 class 层次结构中摆脱这个样板代码?
How to get rid of this boilerplate code in this sealed class hierarchy?
假设我有一个这样的 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
18=]。例如,
fun foo(a: A): A { a.copyX(100) }
上面的定义可能有效,但在所有数据 类 中重复 copyX
似乎非常笨拙。您建议如何摆脱这种重复的 copyX
?
首先,您可以将 copyX
作为扩展(甚至 A
的成员)实现,以便将代码集中在一个地方,至少避免重复 copyX
密封 class 子类型中的函数:
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
class 或有一个 copy
函数,你也可以用反射来复制它们。为此,您需要从 KClass
中获取 primaryConstructor
或名为 copy
的函数,然后填写调用的参数,按名称查找 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(...)
)来优化这一点(这会更冗长)并缓存反射实体。
假设我有一个这样的 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
18=]。例如,
fun foo(a: A): A { a.copyX(100) }
上面的定义可能有效,但在所有数据 类 中重复 copyX
似乎非常笨拙。您建议如何摆脱这种重复的 copyX
?
首先,您可以将 copyX
作为扩展(甚至 A
的成员)实现,以便将代码集中在一个地方,至少避免重复 copyX
密封 class 子类型中的函数:
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
class 或有一个 copy
函数,你也可以用反射来复制它们。为此,您需要从 KClass
中获取 primaryConstructor
或名为 copy
的函数,然后填写调用的参数,按名称查找 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(...)
)来优化这一点(这会更冗长)并缓存反射实体。