Anular getter para la class de datos de Kotlin

Dada la siguiente class de Kotlin:

data class Test(val value: Int) 

¿Cómo anularía el Int getter para que devuelva 0 si el valor es negativo?

Si esto no es posible, ¿cuáles son algunas técnicas para lograr un resultado adecuado?

Podrías probar algo como esto:

 data class Test(private val _value: Int) { val value = _value get(): Int { return if (field < 0) 0 else field } } assert(1 == Test(1).value) assert(0 == Test(0).value) assert(0 == Test(-1).value) assert(1 == Test(1)._value) // Fail because _value is private assert(0 == Test(0)._value) // Fail because _value is private assert(0 == Test(-1)._value) // Fail because _value is private 
  • En una class de datos, debe marcar los parameters del constructor principal con val o var .

  • Estoy asignando el valor de _value a value para usar el nombre deseado para la propiedad.

  • Definí un acceso personalizado para la propiedad con la lógica que describió.

Después de pasar casi un año integer escribiendo Kotlin a diario, he descubierto que hacer esto con classs de datos es una mala práctica. Son 3 enfoques válidos para esto, y después explicaré por qué el enfoque que otras personas están sugiriendo es malo.

  1. Haga que su lógica empresarial que crea la data class altere el valor para que sea 0 o mayor antes de llamar al constructor con el valor incorrecto. Este es probablemente el mejor enfoque para la mayoría de los casos.

  2. No use una data class . Use una class regular y hashCode que su IDE genere los methods equals y hashCode por usted (o no, si no los necesita). Sí, tendrá que volver a generarlo si alguna de las properties cambia en el object, pero se le deja el control total del object.

     class Test(value: Int) { val value: Int = value get() = if (field < 0) 0 else field override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Test) return false return true } override fun hashCode(): Int { return javaClass.hashCode() } } 
  3. Cree una propiedad segura adicional en el object que haga lo que desee en lugar de tener un valor privado que se invalide de manera efectiva.

     data class Test(val value: Int) { val safeValue: Int get() = if (value < 0) 0 else value } 

Un mal enfoque que sugieren otras respuestas:

 data class Test(private val _value: Int) { val value: Int get() = if (_value < 0) 0 else _value } 

El problema con este enfoque es que las classs de datos en realidad no están destinadas a alterar datos como este. En realidad son solo para almacenar datos. Reemplazar el getter por una class de datos como esta significaría que Test(0) y Test(-1) no serían equal entre sí y tendrían diferentes hashCode , pero cuando .value , tendrían el mismo resultado. Esto es inconsistente, y si bien puede funcionar para usted, otras personas en su equipo que ven esto es una class de datos, puede usarlo accidentalmente sin darse count de cómo lo ha alterado / hecho que no funcione como se esperaba (es decir, este enfoque no lo haría) t funciona correctamente en un Map o un Set ).

La respuesta depende de las capacidades que realmente usa que brindan los data . @EPadron mencionó un ingenioso truco (versión mejorada):

 data class Test(private val _value: Int) { val value: Int get() = if (_value < 0) 0 else _value } 

Eso funcionará como se espera, ei tiene un campo, un getter, right equals , hashcode y component1 . El problema es que toString y copy son raros:

 println(Test(1)) // prints: Test(_value=1) Test(1).copy(_value = 5) // <- weird naming 

Para solucionar el problema con toString , puede networkingefinirlo con las manos. No conozco ninguna forma de corregir los nombres de los parameters, pero no utilizar los data en absoluto.

Puedes simplemente

 data class Test(private val number: Int) { var value = number get(){ return if (field < 0) 0 else field } } 

Por favor refiérase a Propiedades y Campos

Esto parece ser uno (entre otros) inconvenientes molestos de Kotlin.

Parece que la única solución razonable, que mantiene por completo la compatibilidad con versiones anteriores de la class, es convertirla en una class regular (no en una class de "datos"), e implementar a mano (con la ayuda del IDE) los methods: hashCode ( ), igual a (), toString (), copy () y componentN ()

 class Data3(i: Int) { var i: Int = i override fun equals(other: Any?): Boolean { if (this === other) return true if (other?.javaClass != javaClass) return false other as Data3 if (i != other.i) return false return true } override fun hashCode(): Int { return i } override fun toString(): String { return "Data3(i=$i)" } fun component1():Int = i fun copy(i: Int = this.i): Data3 { return Data3(i) } } 

Sé que esta es una vieja pregunta, pero parece que nadie mencionó la posibilidad de hacer que el valor sea privado y escribir getter personalizado así:

 data class Test(private val value: Int) { fun getValue(): Int = if (value < 0) 0 else value } 

Esto debería ser perfectamente válido ya que Kotlin no generará un getter pnetworkingeterminado para el campo privado.

Pero, de lo contrario, definitivamente estoy de acuerdo con spierce7 en que las classs de datos son para almacenar datos y debe evitar la encoding rígida de la lógica de "negocios" allí.

  • Cómo analizar JSON manualmente en Kotlin?
  • Extienda la comprobación de Mockito para que Kotlin no funcione (de manera "kotlin")
  • ¿Por qué sumBy (selector) no devuelve Long?
  • Clase Derivada Parcelable en Kotlin
  • Crear un nuevo object de module de nodo de kotlin
  • Retrofit API call: ¿Cómo asegurarse de que el valor no sea nulo después de realizar una llamada de API?
  • Procesamiento de annotations de Kotlin: ¿es posible generar un método de extensión?
  • No se requiere error al definir una variable local nula y configurarla más tarde en el método
  • Kotlin Android Project no pudo sincronizar el Proyecto Gradle
  • ¿Cómo puedo anular un método de Java y cambiar la capacidad de anulación de un parámetro?
  • RxJava Observable a Completable, cómo evitar toBlocking ()