Несколько конструкторов в неизменяемом классе (data)
Я пытаюсь реализовать неизменяемый класс данных с несколькими конструкторами. Я чувствовал, что нечто подобное должно быть возможно:
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)
}
}
Конечно, это не так: Котлин ожидает, что вызов главного конструктора будет объявлен сверху:
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?2 ответа:
Как предложил @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
на сопутствующем объекте (так же, как Scalaapply
) , можно получить не настоящий конструктор, а фабрику, которая выглядит как конструктор usage-site: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")
воля к ожидаемой вещи.