扩展Kotlin中的数据类

时间:2014-10-18 20:18:38

标签: inheritance kotlin data-class

数据类似乎是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()

10 个答案:

答案 0 :(得分:95)

事实是:数据类在继承方面不能很好地发挥作用。我们正在考虑禁止或严格限制数据类的继承。例如,它已知无法在非抽象类的层次结构中正确实现equals()

所以,我可以提供:不要在数据类中使用继承。

答案 1 :(得分:73)

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

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

答案 2 :(得分:16)

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

如果您不喜欢抽象类,如何使用界面

Kotlin中的接口可以具有属性,如本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

我很好奇科特林是如何编译的。这是等效的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(), ...
}

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

答案 3 :(得分:2)

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

如果抽象类内部有抽象子类,那么在数据类中,我们将无法扩展这些抽象子类的字段。我们应该首先创建 data子类,然后定义字段。

<script src="https://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="https://unpkg.com/cytoscape@3.1.0/dist/cytoscape.min.js"></script>

<!-- for testing with local version of cytoscape.js -->
<!--<script src="../cytoscape.js/build/cytoscape.js"></script>-->
<script src="https://unpkg.com/cytoscape-cose-bilkent@4.0.0/cytoscape-cose-bilkent.js"></script>
<script src="https://unpkg.com/cytoscape-expand-collapse@3.1.1/cytoscape-expand-collapse.js"></script>
<div id="cy"></div>

答案 4 :(得分:1)

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

答案 5 :(得分: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()上调用ResourceId可能会产生一个很好的小表示形式,该表示形式通常对所有子类型有效:/users/4587/locations/23等。不幸的是,因为没有一个子类型继承了抽象基础的toString()方法ResourceId,调用toString()实际上会产生不太漂亮的表示形式:<UserResourceId(id=UserId(value=4587))><LocationResourceId(id=LocationId(value=23))>

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

答案 6 :(得分:0)

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

基类

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

有效。

答案 7 :(得分:0)

科特琳特质可以提供帮助。

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

答案 8 :(得分:0)

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

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

答案 9 :(得分:0)

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

//这里是check the image why it shows error id:1 (compiler says that use = insted of double dot where i insert the value)下面的输出