2

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

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" } )
}

但这将检查断言为时已晚,并且不能很好地扩展。

我看到接近所需行为的唯一方法是使用辅助函数(不能定义为非静态 on Color):

constructor(hex: String): this(hexExtract(hex, 1..2), 
                               hexExtract(hex, 3..4), 
                               hexExtract(hex, 5..6))

这并没有让我觉得这是一个非常优雅的模式,所以我猜我在这里遗漏了一些东西。

在 Kotlin 中的不可变数据类上是否有一种优雅、惯用的方式来拥有(复杂的)辅助构造函数?

4

2 回答 2

5

正如@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")

于 2017-03-26T15:21:53.680 回答
4

正如这里所解释的,在伴生对象上使用操作符函数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")将到预期的事情。

于 2017-03-30T21:13:58.343 回答