web-dev-qa-db-fra.com

Étendre la classe de données à Kotlin

Les classes de données semblent remplacer les anciens POJO en Java. Il est tout à fait prévisible que ces classes autorisent l'héritage, mais je ne vois aucun moyen pratique d'étendre une classe de données. Ce dont j'ai besoin, c'est quelque chose comme ça:

open data class Resource (var id: Long = 0, var location: String = "")
data class Book (var isbn: String) : Resource()

Le code ci-dessus échoue à cause d'un conflit de méthodes component1(). Laisser l'annotation data dans une seule des classes ne fait pas non plus le travail.

Peut-être existe-t-il un autre langage pour étendre les classes de données?

UPD: Je pourrais uniquement annoter la classe enfant, mais l'annotation data ne gère que les propriétés déclarées dans le constructeur. C'est-à-dire que je devrais déclarer toutes les propriétés du parent open et les remplacer, ce qui est moche:

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()
96
Dmitry

La vérité est que les classes de données ne jouent pas trop bien avec l'héritage. Nous envisageons d'interdire ou de restreindre sévèrement l'héritage de classes de données. Par exemple, on sait qu'il n'y a aucun moyen d'implémenter equals() correctement dans une hiérarchie de classes non abstraites. 

Donc, tout ce que je peux offrir: n'utilisez pas l'héritage avec des classes de données.

82
Andrey Breslav

Déclarez les propriétés de la super-classe en dehors du constructeur comme abstraites et remplacez-les dans la sous-classe.

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()
62
Željko Trogrlić

La solution ci-dessus utilisant une classe abstraite génère la classe correspondante et laisse la classe de données s’y étendre.

Si vous ne préférez pas une classe abstraite, pourquoi ne pas utiliser un interface?

L'interface dans Kotlin peut avoir properties comme indiqué dans cet article cet article ..

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

J'étais curieux de voir comment Kotlin compile cela. Voici le code Java équivalent (généré à l'aide de la fonction Intellij [code de Kotlin]):

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(), ...
}

Comme vous pouvez le constater, cela fonctionne exactement comme une classe de données normale!

13
Tura

@ Željko Trogrlić répond correctement. Mais nous devons répéter les mêmes champs que dans une classe abstraite. 

De même, si nous avons sous-classes abstraites dans la classe abstraite, dans une classe de données, nous ne pouvons pas étendre les champs de ces sous-classes abstraites. Nous devrions d’abord créer data subclass puis définir des champs.

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()
}
1
CoolMind

Les traits de Kotlin peuvent aider.

interface IBase {
    val prop:String
}

interface IDerived : IBase {
    val derived_prop:String
}

classes de données

data class Base(override val prop:String) : IBase

data class Derived(override val derived_prop:String,
                   private val base:IBase) :  IDerived, IBase by base

exemple d'utilisation

val b = Base("base")
val d = Derived("derived", b)

print(d.prop) //prints "base", accessing base class property
print(d.derived_prop) //prints "derived"

Cette approche peut également constituer une solution de contournement pour les problèmes d’héritage avec @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
1
Jegan Babu

Vous pouvez hériter d'une classe de données d'une classe autre que de données. L'héritage d'une classe de données d'une autre classe de données n'est pas autorisé car il n'existe aucun moyen de faire en sorte que les méthodes de classes de données générées par le compilateur fonctionnent de manière cohérente et intuitive en cas d'héritage.

1
ABr

Vous pouvez hériter d'une classe de données d'une classe autre que de données.

Classe de base

open class BaseEntity (

@ColumnInfo(name = "name") var name: String? = null,
@ColumnInfo(name = "description") var description: String? = null,
// ...
)

classe enfant

@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()

Ça a marché.

0
tim4dev