Varios constructores en una class inmutable (datos)

Estoy tratando de implementar una class de datos inmutables con más de un constructor. Sentí que algo así debería ser posible:

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

Por supuesto, no lo es: Kotlin espera que la llamada al constructor principal se declare en la parte superior:

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

Tampoco es bueno, ya que la llamada se ejecuta antes que el cuerpo constructor y no puede acceder a las variables locales.

Puedo hacer esto , por supuesto:

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

Pero esto verificará la afirmación demasiado tarde y no se escalará muy bien.

La única forma en que veo acercarme al comportamiento deseado es usando una function auxiliar (que no se puede definir no estática en Color ):

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

Esto no me parece un patrón muy elegante, así que supongo que me estoy perdiendo algo aquí.

¿Existe una forma elegante e idiomática de tener constructores secundarios (complejos) en classs de datos inmutables en Kotlin?

Como lo sugirió @nhaarman, una forma de hacerlo es usar un método de fábrica. A menudo uso algo como lo siguiente:

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

Y luego puedes llamarlo con Color.fromHex("#abc123")

Como se explica aquí , al utilizar la invoke function de operador en el object complementario (al igual que la apply de Scala) uno puede lograr no realmente un constructor, sino una fábrica que se parece a un sitio de uso de constructor:

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

Ahora, Color("#FF00FF") hará lo esperado.

  • La anotación de ButterKnife @OnClick no funciona en el fragment de Kotlin
  • no puede generar carpetas de vista java.lang.NullPointerException
  • Extraño error de "Referencia no resuelto:" después de actualizar a Kotlin 1.0.5
  • Cómo alterar las properties del object de testing en KotlinTest a través de interceptTestCase
  • Kotlin: reference no resuelta: javaClass
  • ¿Cómo iniciar dos constructores en kotlin con class interna?
  • Cómo agregar varias instrucciones dentro de una instrucción when en kotlin
  • Retrofit2 Añadir cadena adicional al object Body
  • En Kotlin, ¿es posible cambiar la delegación en Runtime?
  • Kotlin cuando () introducción de variable local
  • ¿Cómo vincular a un miembro estático de una class de Java en KDoc?