279

数据类似乎是 Java 中老式 POJO 的替代品。这些类允许继承是可以预料的,但我看不出扩展数据类的方便方法。我需要的是这样的:

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

上面的代码由于component1()方法冲突而失败。只在其中一个类中留下data注释也不起作用。

也许还有另一种方式来扩展数据类?

UPD:我可能只注释子子类,但data注释只处理构造函数中声明的属性。也就是说,我必须声明所有父级的属性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()
4

11 回答 11

255

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

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

于 2014-10-20T13:53:56.240 回答
169

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

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()
于 2017-08-18T12:25:59.607 回答
39

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

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

Kotlin 中的接口可以具有本文中所示的属性..

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

我很好奇 Kotlin 是如何编译这个的。这是等效的 Java 代码(使用 Intellij [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(), ...
}

如您所见,它的工作方式与普通数据类完全一样!

于 2018-07-10T15:38:46.223 回答
10

Kotlin Traits 可以提供帮助。

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
于 2019-06-27T05:57:20.887 回答
6

@Ž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()
}
于 2019-01-29T09:41:56.003 回答
4

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

于 2018-09-26T06:42:04.390 回答
2

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

基类

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

有效。

于 2019-06-09T19:05:21.863 回答
1

我是怎么做到的。

open class ParentClass {
var var1 = false
var var2: String? = null
}

data class ChildClass(
    var var3: Long
) : ParentClass()

它工作正常。

于 2021-09-10T18:34:56.133 回答
1

虽然equals()在层次结构中正确实现确实很麻烦,但支持继承其他方法仍然很好,例如:toString().

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

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)

假设我们UserLocation实体分别返回它们适当的资源 ID(UserResourceIdLocationResourceId),调用toString()anyResourceId可能会产生一个相当不错的小表示,它通常对所有子类型都有效:/users/4587/locations/23等。不幸的是,因为没有继承到toString()从abstract base ResourceId,调用toString()实际上会导致不太漂亮的表示:<UserResourceId(id=UserId(value=4587))><LocationResourceId(id=LocationId(value=23))>

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

于 2020-01-04T22:57:10.573 回答
-1

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

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

于 2021-04-10T09:49:23.297 回答
-1
data class User(val id:Long, var name: String)
fun main() {
val user1 = User(id:1,name:"Kart")
val name = user1.name
println(name)
user1.name = "Michel"
val  user2 = User(id:1,name:"Michel")
println(user1 == user2)
println(user1)
val updateUser = user1.copy(name = "DK DK")
println(updateUser)
println(updateUser.component1())
println(updateUser.component2())
val (id,name) = updateUser
println("$id,$name") }

//这里是下面的输出 检查图像为什么它显示错误id:1(编译器说use = insted of double dot where I insert the value)

于 2021-06-24T22:15:48.890 回答