扩展Kotlin中的数据类

Dmi*_*try 141 inheritance android abstract kotlin data-class

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

open data class Resource (var id: Long = 0, var location: String = "")
data class Book (var isbn: String) : Resource()
Run Code Online (Sandbox Code Playgroud)

由于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()
Run Code Online (Sandbox Code Playgroud)

And*_*lav 126

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

所以,我可以提供:不要对数据类使用继承.

  • 我不相信这个问题有很多解决办法.到目前为止,我的观点是数据类根本不能有数据子类. (3认同)
  • 如果我们有一些库代码,例如某些ORM,我们想扩展其模型以获得持久数据模型,该怎么办? (3认同)
  • @AndreyBreslav [数据类文档](https://kotlinlang.org/docs/reference/data-classes.html)不反映Kotlin 1.1之后的状态.自1.1以来,数据类和继承如何一起发挥作用? (3认同)
  • @EugenPechanec请参阅此示例:https://kotlinlang.org/docs/reference/whatsnew11.html#sealed-and-data-classes (2认同)
  • 如果我们不能对数据类使用继承,则意味着当逻辑相同而数据不同时会出现大量重复代码......由于缺乏继承支持,我正在复制大量代码,非常非常糟糕 (2认同)

Žel*_*lić 88

将构造函数之外的超类中的属性声明为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()
Run Code Online (Sandbox Code Playgroud)

  • 这似乎是最灵活的.我非常希望我们可以让数据类继承彼此... (9认同)
  • 复制参数似乎是实现继承的一种糟糕方法。从技术上讲,由于 Book 继承自 Resource,因此它应该知道 id 和 location 存在。确实不需要指定这些。 (7认同)
  • 我几乎失去了希望。谢谢! (2认同)
  • 这适用于 Kotlin,但如果我必须从 Java 调用数据类构造函数,我会收到“无法从最终 [类] 继承”编译错误。这是为什么?可以解决吗? (2认同)

Tur*_*ura 20

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

如果你不喜欢抽象类,那么使用接口呢?

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
Run Code Online (Sandbox Code Playgroud)

我很好奇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(), ...
}
Run Code Online (Sandbox Code Playgroud)

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

  • 不幸的是,为数据类实现接口模式不适用于 Room 的架构。 (5认同)
  • @Adam Hurwitz 刚刚遇到这个问题,你能解释一下为什么吗? (3认同)

Jeg*_*abu 8

Kotlin Traits 可以提供帮助。

interface IBase {
    val prop:String
}

interface IDerived : IBase {
    val derived_prop:String
}
Run Code Online (Sandbox Code Playgroud)

数据类

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

data class Derived(override val derived_prop:String,
                   private val base:IBase) :  IDerived, IBase by base
Run Code Online (Sandbox Code Playgroud)

示例用法

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

print(d.prop) //prints "base", accessing base class property
print(d.derived_prop) //prints "derived"
Run Code Online (Sandbox Code Playgroud)

这种方法也可以解决@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
Run Code Online (Sandbox Code Playgroud)


Coo*_*ind 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()
}
Run Code Online (Sandbox Code Playgroud)


tim*_*dev 6

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

基类

open class BaseEntity (

@ColumnInfo(name = "name") var name: String? = null,
@ColumnInfo(name = "description") var description: String? = null,
// ...
)
Run Code Online (Sandbox Code Playgroud)

儿童班

@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()
Run Code Online (Sandbox Code Playgroud)

有效。

  • 不幸的是,将为该数据类生成的“equals()”、“hashCode()”和“toString()”将不包含基类的属性。这消除了在这里使用数据类的好处。 (7认同)
  • 除了现在您无法设置名称和描述属性之外,如果将它们添加到构造函数中,数据类需要 val/var ,它将覆盖基类属性。 (6认同)

sha*_*aby 5

我是怎么做到的。

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

data class ChildClass(
  var var3: Long
) : ParentClass()
Run Code Online (Sandbox Code Playgroud)

运行良好。

  • 如果您想要要求构造每个 ChildClass 并传递 var1 和 var2 的值,您将如何构造 ChildClass? (10认同)