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.

  • Anotación de Android con Kotlin
  • Errores HashMap - containsKey, get
  • ¿Cuál es la diferencia entre manejar campos a través de "accesor" y "copyr"
  • Clase de datos de Kotlin que implementa la interfaz de Java
  • Deshabilitar un button
  • ¿Puedo crear un método de extensión Kotlin para agregar una suscripción rxJava a una suscripción compuesta?
  • Representación de imágenes con subprocesss múltiples en JavaFx / Kotlin
  • java.lang.IllegalStateException ¿No está seguro de que sea con el tipo de datos?
  • lanzar si el operador en Kotlin
  • Dependencias faltantes o conflictivas para Anko floatingActionButton
  • Agregar idioma al keyboard sin abrir la configuration