不可变(数据)类上的多个构造函数

我试图用多个构造函数实现一个不可变的数据类。 我觉得这样的事情应该是可能的:

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上拥有不可变数据类上的(复杂)次级构造函数?

正如@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")来调用它

正如在这里解释的那样,使用对伴侣对象的操作符函数invoke (就像Scala的apply ),可以实现一个不是真正的构造函数,而是一个看起来像构造函数usage-site的工厂:

 companion object { operator fun invoke(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) Color(r, g, b) } } 

现在, Color("#FF00FF")将会达到预期的效果。