Kotlinのdata object

Kenji Abe
7 min readApr 10, 2023

--

Photo by Markus Winkler on Unsplash

Kotlin 1.9でリリース予定で、これを書いてる時点の最新バージョン(1.8.20)ではPreview機能として使用できる data object について簡単にまとめておきます。

基本はドキュメントを読んでもらえば良いかなとは思います。

通常の object との違いなどを見ていきます。

toString()

data object では toString() が定義されるようになり、 println などで出力するときに見やすくなります。

object SampleObject
data object SampleDataObject

fun main() {
println(SampleObject) // => SampleObject@58ceff1
println(SampleDataObject) // => SampleDataObject
}

Decompileすると以下のように実装されていることが確認できます。

public final class SampleDataObject {
// ...

@NotNull
public String toString() {
return "SampleDataObject";
}
}

equals と hashCode

ほとんどのケースで object はSingletonとして扱われるので、 == で同じ object を比較した場合は true を返します。これは data object でも同様です。

object SampleObject
data object SampleDataObject

fun main() {
println(SampleObject == SampleObject) // => true
println(SampleDataObject == SampleDataObject) // => true
}

ただし、エッジケースとしてリフレクションを使用して別のインスタンスを作ることが可能になっています。このとき object の場合は == で比較したときは false を返すようになっています。

object SampleObject

fun main() {
val newInstance = (SampleObject.javaClass.declaredConstructors[0].apply {
isAccessible = true
} as Constructor<SampleObject>).newInstance()

println(SampleObject == newInstance) // => false
}

これと同じことを data object でやった場合に == の比較の結果は true になります。ただし、 === の比較結果は false になります。

data object SampleDataObject

fun main() {
val newInstance = (SampleDataObject.javaClass.declaredConstructors[0].apply {
isAccessible = true
} as Constructor<SampleDataObject>).newInstance()

println(SampleDataObject == newInstance) // => true
println(SampleDataObject === newInstance) // => false
}

data object をDecompileすると、 hashCodeequals が実装されてるのを確認できます。

public final class SampleDataObject {
// ...

public int hashCode() {
return 1802936563;
}

public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (!(var1 instanceof SampleDataObject)) {
return false;
}
}

return true;
}
}

この equals のコードをみて分かる通り、 instanceof で同じ型の場合はすべて true を返す実装になっています。

data class と data object

data class では copycomponentN のメソッドが生成されますが、 data object では生成されません。

sealed class/interface

以下のように sealed class / sealed interfacedata classobject を使うケースは結構あると思います。

sealed interface Sample

data class DataClass(val id: Int) : Sample
object DataObject : Sample

このとき、 data object を使用することで、 println 等で出力したときに data class と同じような出力になります。

sealed interface Sample

data class DataClass(val id: Int) : Sample
data object DataObject : Sample

fun main() {
println(DataClass(1)) // => DataClass(id=1)
println(DataObject) // => DataObject
}

また、IntelliJ では以下のような警告を表示して data object の使用を勧めてきます。

object と data object の使い分け

個人的には data object のほうがメリットが多く、今後は object を使う理由は減ってくるかなと思います。

このあたり何か考えある方がいましたら教えてもらえると嬉しいです。

参考

--

--

Kenji Abe

Programmer / Gamer / Google Developers Expert for Android, Kotlin / @STAR_ZERO