Inheritance 在Kotlin中扩展数据类

Inheritance 在Kotlin中扩展数据类,inheritance,kotlin,abstract,data-class,Inheritance,Kotlin,Abstract,Data Class,数据类似乎是Java中老式POJO的替代品。很可能这些类会允许继承,但我看不到扩展数据类的方便方法。我需要的是这样的东西: open data class Resource (var id: Long = 0, var location: String = "") data class Book (var isbn: String) : Resource() 由于component1()方法冲突,上述代码失败。只在其中一个类中保留数据注释也不起作用 也许还有另一种扩展数据类的习惯用法 UPD:

数据类似乎是Java中老式POJO的替代品。很可能这些类会允许继承,但我看不到扩展数据类的方便方法。我需要的是这样的东西:

open data class Resource (var id: Long = 0, var location: String = "")
data class Book (var isbn: String) : Resource()
由于
component1()
方法冲突,上述代码失败。只在其中一个类中保留
数据
注释也不起作用

也许还有另一种扩展数据类的习惯用法

UPD:我可能只注释一个子类,但是
数据
注释只处理构造函数中声明的属性。也就是说,我必须声明所有父级的属性
open
并覆盖它们,这很难看:

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

事实是:数据类不能很好地处理继承。我们正在考虑禁止或严格限制数据类的继承。例如,众所周知,在非抽象类的层次结构中,无法正确实现
equals()


所以,我所能提供的就是:不要对数据类使用继承。

在构造函数之外的超类中将属性声明为抽象属性,并在子类中重写它们

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

上述使用抽象类的解决方案实际上生成了相应的类,并让数据类从中扩展

如果您不喜欢抽象类,那么使用接口如何

Kotlin中的接口可以具有如下所示的属性

我很好奇Kotlin是如何编译的。下面是等效的Java代码(使用Intellij[Kotlin bytecode]功能生成):


如您所见,它的工作方式与普通数据类完全相同

您可以从非数据类继承数据类。不允许从另一个数据类继承数据类,因为在继承的情况下,无法使编译器生成的数据类方法一致直观地工作。

@eljko Trogrlić的答案是正确的。但是我们必须重复与抽象类中相同的字段

此外,如果我们在抽象类中有抽象子类,那么在数据类中,我们不能从这些抽象子类扩展字段。我们应该首先创建数据子类,然后定义字段

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()
}
抽象类抽象类{
抽象val代码:Int
抽象值url:字符串?
抽象val错误:错误?
抽象类错误{
抽象val消息:列表?
}
}
数据类历史记录(
val数据:字符串?,
覆盖val代码:Int,
覆盖val url:字符串?,
//不要在这里扩展AbstractClass.Errors,但Kotlin允许这样做。
覆盖val错误:错误?
):AbstractClass(){
//在此扩展一个数据类,然后可以将其用于“errors”字段。
数据类错误(
覆盖val消息:列表?
):AbstractClass.Errors()
}

您可以从非数据类继承数据类

基类

open class BaseEntity (

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

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

它起作用了。

科特林的特质会有所帮助

interface IBase {
    val prop:String
}

interface IDerived : IBase {
    val derived_prop:String
}
数据类

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

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

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

print(d.prop) //prints "base", accessing base class property
print(d.derived_prop) //prints "derived"
这种方法也可以作为@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

虽然在层次结构中正确地实现
equals()
确实是一个难题,但支持继承其他方法还是不错的,例如:
toString()

更具体地说,让我们假设我们有以下构造(显然,它不起作用,因为
toString()
不是继承的,但是如果它可以继承,那不是很好吗?)

假设我们的
User
Location
实体返回其相应的资源ID(
UserResourceId
LocationResourceId
),调用
toString()
在任何
资源ID
上都可能产生一个非常好的小表示,通常对所有子类型都有效:
/users/4587
/locations/23
,等等。不幸的是,由于没有子类型继承到从抽象基
资源ID
重写的
toString()
方法,调用
toString()
实际上会导致不太漂亮的表示:


还有其他方法可以对上述内容进行建模,但这些方法要么迫使我们使用非数据类(错过了数据类的许多好处),要么我们最终在所有数据类中复制/重复
toString()
实现(无继承).

我发现在DTO中使用继承的最佳方法是使用插件在java中创建数据类


不要忘记在注释中将lombok.equalsAndHashCode.callSuper设置为true

Kotlin隐式创建返回第N个属性值的方法
componentN()
。请参阅上的文档以打开属性,您还可以将资源抽象化或使用编译器插件。Kotlin对开/闭原则要求严格。@Dmitry因为我们无法扩展数据类,您保持父类变量打开并在子类中简单重写它们的“解决方案”是否可以解决这个问题?我认为这个问题没有太多解决方案。到目前为止,我的观点是,数据类不能有任何数据子类。如果我们有一个库代码,比如某个ORM,并且我们想要扩展它的模型以拥有我们的持久数据模型,该怎么办?@AndreyBreslav并不反映Kotlin 1.1之后的状态。自1.1版以来,数据类和继承是如何结合在一起的?@EugenPechanec看到了这个例子:如果我们不能对数据类使用继承,那就意味着在逻辑相同而数据不同的情况下会有大量重复的代码……由于缺乏继承支持,我复制了大量的代码,非常糟糕,这似乎是最灵活的。我真的非常希望我们可以让数据类彼此继承……您好,先生,感谢您以简洁的方式处理数据类继承。当我将抽象类用作泛型类型时,我面临一个问题。我得到一个
类型的Mism
val b = Base("base")
val d = Derived("derived", b)

print(d.prop) //prints "base", accessing base class property
print(d.derived_prop) //prints "derived"
@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
abstract class ResourceId(open val basePath: BasePath, open val id: Id) {

    // non of the subtypes inherit this... unfortunately...
    override fun toString(): String = "/${basePath.value}/${id.value}"
}
data class UserResourceId(override val id: UserId) : ResourceId(UserBasePath, id)
data class LocationResourceId(override val id: LocationId) : ResourceId(LocationBasePath, id)