web-dev-qa-db-ja.com

Kotlinでデータクラスを拡張する

データクラスは、Javaの昔ながらのPOJOの代わりになるようです。これらのクラスが継承を許可することは非常に期待できますが、データクラスを拡張する便利な方法はありません。私が必要なのは次のようなものです:

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

component1()メソッドの衝突のため、上記のコードは失敗します。 dataアノテーションをクラスの1つだけに残しても、作業は行われません。

おそらく、データクラスを拡張する別のイディオムがありますか?

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

真実は次のとおりです。データクラスは継承ではあまりうまく機能しません。データクラスの継承を禁止または厳しく制限することを検討しています。たとえば、非抽象クラスの階層にequals()を正しく実装する方法がないことが知られています。

したがって、私が提供できるのは、データクラスで継承を使用しないことです。

111
Andrey Breslav

コンストラクターの外部のスーパークラスのプロパティを抽象として宣言し、サブクラスでオーバーライドします。

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

上記の抽象クラスを使用したソリューションでは、実際に対応するクラスが生成され、そこからデータクラスが拡張されます。

抽象クラスを好まない場合は、interfaceを使用してはどうですか?

Kotlinのインターフェイスは、この この記事 。に示すようにpropertiesを持つことができます。

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

ご覧のとおり、通常のデータクラスとまったく同じように機能します。

19
Tura

@ŽeljkoTrogrlićの答えは正しいです。しかし、抽象クラスの場合と同じフィールドを繰り返す必要があります。

また、抽象クラス内にabstract subclassesがある場合、データクラスではこれらの抽象サブクラスからフィールドを拡張できません。最初にdata subclassを作成してから、フィールドを定義する必要があります。

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

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
1
Jegan Babu

非データクラスからデータクラスを継承できます。別のデータクラスからデータクラスを継承することは許可されません。継承の場合、コンパイラによって生成されたデータクラスメソッドを一貫して直感的に動作させる方法がないためです。

1
Abraham Mathew

非データクラスからデータクラスを継承できます。

基本クラス

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

動いた。

0
tim4dev