inheritance - data - Ampliar clase de datos en Kotlin
kotlin data class setter (7)
Las clases de datos parecen ser el reemplazo de los POJO anticuados en Java. Es bastante esperable que estas clases permitan la herencia, pero no veo una manera conveniente de extender una clase de datos. Lo que necesito es algo como esto:
open data class Resource (var id: Long = 0, var location: String = "")
data class Book (var isbn: String) : Resource()
El código anterior falla debido al choque de los métodos del
component1()
.
Dejar
data
anotación de
data
en solo una de las clases tampoco funciona.
¿Quizás hay otro idioma para extender las clases de datos?
UPD: podría anotar solo la clase secundaria hijo, pero
data
anotación de
data
solo maneja las propiedades declaradas en el constructor.
Es decir, tendría que declarar
open
todas las propiedades de los padres y anularlas, lo cual es feo:
open class Resource (open var id: Long = 0, open var location: String = "")
data class Book (
override var id: Long = 0,
override var location: String = "",
var isbn: String
) : Resource()
Declare las propiedades en la superclase fuera del constructor como abstractas y anúlelas en la subclase.
abstract class Resource {
abstract var id: Long
abstract var location: String
}
data class Book (
override var id: Long = 0,
override var location: String = "",
var isbn: String
) : Resource()
La respuesta de @ Željko Trogrlić es correcta. Pero tenemos que repetir los mismos campos que en una clase abstracta.
Además, si tenemos subclases abstractas dentro de la clase abstracta, en una clase de datos no podemos extender los campos de estas subclases abstractas. Primero debemos crear una subclase de datos y luego definir campos.
abstract class AbstractClass {
abstract val code: Int
abstract val url: String?
abstract val errors: Errors?
abstract class Errors {
abstract val messages: List<String>?
}
}
data class History(
val data: String?,
override val code: Int,
override val url: String?,
// Do not extend from AbstractClass.Errors here, but Kotlin allows it.
override val errors: Errors?
) : AbstractClass() {
// Extend a data class here, then you can use it for ''errors'' field.
data class Errors(
override val messages: List<String>?
) : AbstractClass.Errors()
}
La solución anterior que usa la clase abstracta en realidad genera la clase correspondiente y deja que la clase de datos se extienda desde ella.
Si no prefiere la clase abstracta, ¿qué tal usar una interfaz ?
La interfaz en Kotlin puede tener propiedades como se muestra en este artículo .
interface History {
val date: LocalDateTime
val name: String
val value: Int
}
data class FixedHistory(override val date: LocalDateTime,
override val name: String,
override val value: Int,
val fixedEvent: String) : History
Tenía curiosidad por cómo Kotlin compila esto. Aquí hay un código Java equivalente (generado utilizando la función Intellij [Kotlin bytecode]):
public interface History {
@NotNull
LocalDateTime getDate();
@NotNull
String getName();
int getValue();
}
public final class FixedHistory implements History {
@NotNull
private final LocalDateTime date;
@NotNull
private final String name;
private int value;
@NotNull
private final String fixedEvent;
// Boring getters/setters as usual..
// copy(), toString(), equals(), hashCode(), ...
}
Como puede ver, ¡funciona exactamente como una clase de datos normal!
La verdad es que las clases de datos no juegan demasiado bien con la herencia.
Estamos considerando prohibir o restringir severamente la herencia de las clases de datos.
Por ejemplo, se sabe que no hay forma de implementar
equals()
correctamente en una jerarquía en clases no abstractas.
Entonces, todo lo que puedo ofrecer: no use la herencia con clases de datos.
Los rasgos de Kotlin pueden ayudar.
interface IBase {
val prop:String
}
interface IDerived : IBase {
val derived_prop:String
}
clases de datos
data class Base(override val prop:String) : IBase
data class Derived(override val derived_prop:String,
private val base:IBase) : IDerived, IBase by base
uso de muestra
val b = Base("base")
val d = Derived("derived", b)
print(d.prop) //prints "base", accessing base class property
print(d.derived_prop) //prints "derived"
Este enfoque también puede ser una solución para problemas de herencia con @Parcelize
@Parcelize
data class Base(override val prop:Any) : IBase, Parcelable
@Parcelize // works fine
data class Derived(override val derived_prop:Any,
private val base:IBase) : IBase by base, IDerived, Parcelable
Puede heredar una clase de datos de una clase que no sea de datos. No se permite heredar una clase de datos de otra clase de datos porque no hay forma de hacer que los métodos de clase de datos generados por el compilador funcionen de manera coherente e intuitiva en caso de herencia.
Puede heredar una clase de datos de una clase que no sea de datos.
Clase base
open class BaseEntity (
@ColumnInfo(name = "name") var name: String? = null,
@ColumnInfo(name = "description") var description: String? = null,
// ...
)
clase infantil
@Entity(tableName = "items", indices = [Index(value = ["item_id"])])
data class CustomEntity(
@PrimaryKey
@ColumnInfo(name = "id") var id: Long? = null,
@ColumnInfo(name = "item_id") var itemId: Long = 0,
@ColumnInfo(name = "item_color") var color: Int? = null
) : BaseEntity()
Funcionó.