Extend data class in Kotlin

假装没事ソ 提交于 2019-11-26 12:09:55

问题


Data classes seem to be the replacement to the old-fashioned POJOs in Java. It is quite expectable that these classes would allow for inheritance, but I can see no convenient way to extend a data class. What I need is something like this:

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

The code above fails because of clash of component1() methods. Leaving data annotation in only one of classes does not do the work, too.

Perhaps there is another idiom to extend data classes?

UPD: I might annotate only child child class, but data annotation only handles properties declared in the constructor. That is, I would have to declare all parent\'s properties open and override them, which is ugly:

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

回答1:


The truth is: data classes do not play too well with inheritance. We are considering prohibiting or severely restricting inheritance of data classes. For example, it's known that there's no way to implement equals() correctly in a hierarchy on non-abstract classes.

So, all I can offer: don't use inheritance with data classes.




回答2:


Declare properties in super-class outside of constructor as abstract, and override them in sub-class.

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



回答3:


Above solution using abstract class actually generates corresponding class and let the data class extends from it.

If you don't prefer abstract class, how about using an interface?

Interface in Kotlin can have properties as shown in this this 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

I was curious how Kotlin compile this. Here's equivalent Java code (generated using the Intellij [Kotlin bytecode] feature):

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

As you can see, it works exactly like a normal data class!




回答4:


@Željko Trogrlić answer is correct. But we have to repeat the same fields as in an abstract class.

Also if we have abstract subclasses inside the abstract class, then in a data class we cannot extend fields from these abstract subclasses. We should first create data subclass and then define fields.

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



回答5:


You can inherit a data class from a non-data class. Inheriting a data class from another data class is not allowed because there is no way to make compiler-generated data class methods work consistently and intuitively in case of inheritance.




回答6:


Kotlin Traits can help.

interface IBase {
    val prop:String
}

interface IDerived : IBase {
    val derived_prop:String
}

data classes

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

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

sample usage

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

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

This approach can also be a workaround for inheritance issues with @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



回答7:


You can inherit a data class from a non-data class.

Base class

open class BaseEntity (

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

child class

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

It worked.



来源:https://stackoverflow.com/questions/26444145/extend-data-class-in-kotlin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!