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すると、 hashCode
と equals
が実装されてるのを確認できます。
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
では copy
と componentN
のメソッドが生成されますが、 data object
では生成されません。
sealed class/interface
以下のように sealed class
/ sealed interface
で data class
と object
を使うケースは結構あると思います。
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
を使う理由は減ってくるかなと思います。
このあたり何か考えある方がいましたら教えてもらえると嬉しいです。