不可变(数据)上的多个构造函数 class
Multiple constructors on an immutable (data) class
我正在尝试使用多个构造函数实现不可变数据 class。我觉得这样的事情应该是可能的:
data class Color(val r: Int, val g: Int, val b: Int) {
constructor(hex: String) {
assert(Regex("#[a-fA-F0-6]{6}").matches(hex), { "$hex is not a hex color" } )
val r = hex.substring(1..2).toInt(16)
val g = hex.substring(3..4).toInt(16)
val b = hex.substring(5..6).toInt(16)
this(r,g,b)
}
}
当然不是:Kotlin 希望在顶部声明对主构造函数的调用:
constructor(hex: String): this(r,g,b) {
assert(Regex("#[a-fA-F0-6]{6}").matches(hex), { "$hex is not a hex color" } )
val r = hex.substring(1..2).toInt(16)
val g = hex.substring(3..4).toInt(16)
val b = hex.substring(5..6).toInt(16)
}
这也不好,因为调用在之前构造函数主体执行,无法访问局部变量。
我可以这个,当然:
constructor(hex: String): this(hex.substring(1..2).toInt(16),
hex.substring(3..4).toInt(16),
hex.substring(5..6).toInt(16)) {
assert(Regex("#[a-fA-F0-6]{6}").matches(hex), { "$hex is not a hex color" } )
}
但是这会检查断言太晚,并且不能很好地扩展。
我看到接近所需行为的唯一方法是使用辅助函数(不能在 Color
上定义为非静态):
constructor(hex: String): this(hexExtract(hex, 1..2),
hexExtract(hex, 3..4),
hexExtract(hex, 5..6))
我觉得这不是一个非常优雅的模式,所以我猜我在这里遗漏了什么。
在 Kotlin 中,是否有一种优雅、惯用的方式在不可变数据 classes 上拥有(复杂的)二级构造函数?
正如@nhaarman 所建议的,一种方法是使用工厂方法。我经常使用如下内容:
data class Color(val r: Int, val g: Int, val b: Int) {
companion object {
fun fromHex(hex: String): Color {
assert(Regex("#[a-fA-F0-6]{6}").matches(hex), { "$hex is not a hex color" } )
val r = hex.substring(1..2).toInt(16)
val g = hex.substring(3..4).toInt(16)
val b = hex.substring(5..6).toInt(16)
return Color(r,g,b)
}
}
}
然后你可以用Color.fromHex("#abc123")
调用它
正如所解释的 here,在伴随对象上使用运算符函数 invoke
(就像 Scala 的 apply
)可以实现的不是真正的构造函数,而是 看起来 像一个构造函数使用站点:
companion object {
operator fun invoke(hex: String) : Color {
assert(Regex("#[a-fA-F0-6]{6}").matches(hex),
{"$hex is not a hex color"})
val r = hex.substring(1..2).toInt(16)
val g = hex.substring(3..4).toInt(16)
val b = hex.substring(5..6).toInt(16)
return Color(r, g, b)
}
}
现在,Color("#FF00FF")
将达到预期的效果。
我正在尝试使用多个构造函数实现不可变数据 class。我觉得这样的事情应该是可能的:
data class Color(val r: Int, val g: Int, val b: Int) {
constructor(hex: String) {
assert(Regex("#[a-fA-F0-6]{6}").matches(hex), { "$hex is not a hex color" } )
val r = hex.substring(1..2).toInt(16)
val g = hex.substring(3..4).toInt(16)
val b = hex.substring(5..6).toInt(16)
this(r,g,b)
}
}
当然不是:Kotlin 希望在顶部声明对主构造函数的调用:
constructor(hex: String): this(r,g,b) {
assert(Regex("#[a-fA-F0-6]{6}").matches(hex), { "$hex is not a hex color" } )
val r = hex.substring(1..2).toInt(16)
val g = hex.substring(3..4).toInt(16)
val b = hex.substring(5..6).toInt(16)
}
这也不好,因为调用在之前构造函数主体执行,无法访问局部变量。
我可以这个,当然:
constructor(hex: String): this(hex.substring(1..2).toInt(16),
hex.substring(3..4).toInt(16),
hex.substring(5..6).toInt(16)) {
assert(Regex("#[a-fA-F0-6]{6}").matches(hex), { "$hex is not a hex color" } )
}
但是这会检查断言太晚,并且不能很好地扩展。
我看到接近所需行为的唯一方法是使用辅助函数(不能在 Color
上定义为非静态):
constructor(hex: String): this(hexExtract(hex, 1..2),
hexExtract(hex, 3..4),
hexExtract(hex, 5..6))
我觉得这不是一个非常优雅的模式,所以我猜我在这里遗漏了什么。
在 Kotlin 中,是否有一种优雅、惯用的方式在不可变数据 classes 上拥有(复杂的)二级构造函数?
正如@nhaarman 所建议的,一种方法是使用工厂方法。我经常使用如下内容:
data class Color(val r: Int, val g: Int, val b: Int) {
companion object {
fun fromHex(hex: String): Color {
assert(Regex("#[a-fA-F0-6]{6}").matches(hex), { "$hex is not a hex color" } )
val r = hex.substring(1..2).toInt(16)
val g = hex.substring(3..4).toInt(16)
val b = hex.substring(5..6).toInt(16)
return Color(r,g,b)
}
}
}
然后你可以用Color.fromHex("#abc123")
正如所解释的 here,在伴随对象上使用运算符函数 invoke
(就像 Scala 的 apply
)可以实现的不是真正的构造函数,而是 看起来 像一个构造函数使用站点:
companion object {
operator fun invoke(hex: String) : Color {
assert(Regex("#[a-fA-F0-6]{6}").matches(hex),
{"$hex is not a hex color"})
val r = hex.substring(1..2).toInt(16)
val g = hex.substring(3..4).toInt(16)
val b = hex.substring(5..6).toInt(16)
return Color(r, g, b)
}
}
现在,Color("#FF00FF")
将达到预期的效果。