Kotlin data classのprivateコンストラクタとcopyメソッド

Kenji Abe
6 min readAug 31, 2024

--

Photo by Diana Parkhouse on Unsplash

Kotlinのdata classはコンストラクタをprivateにしたとしても、copyメソッドはprivateにならずに使用可能でした。そのため問題が起きることがありました。2.0.20からはこの問題に対応することができます。

これまで

例えば、以下のようなdata classがあったとします。

data class User private constructor(
val name: String,
val age: Int
) {
companion object {
fun create(name: String, age: Int): User {
require(age in 0..200)
return User(name, age)
}
}
}

コンストラクタはprivateにしています。

このdata classを使用するときに、copyメソッドはprivateになっていないため、以下のようなことが可能です。

val user = User.create(name = "Test", age = 20)

// copyメソッドが使えてしまう
val newUser = user.copy(age = -1)

copyメソッドが使えてしまうため、ファクトリメソッドで制限などをしていても不正な状態が作れてしまいます。

2.0.20から

警告表示

2.0.20からdata classprivateコンストラクタを使ってると、ビルド時に警告が表示されるようになります。

w: <path_to>/Main.kt:3:17 Non-public primary constructor is exposed via the generated 'copy()' method of the 'data' class.

The generated 'copy()' will change its visibility in future releases.

To suppress the warning do one of the following:
- Annotate the data class with the '@ConsistentCopyVisibility' annotation.
- Use the '-Xconsistent-data-class-copy-visibility' compiler flag.
- Annotate the data class with the '@ExposedCopyVisibility' annotation
(Discouraged, but can be used to keep binary compatibility).

To learn more, see the documentation of the '@ConsistentCopyVisibility' and '@ExposedCopyVisibility' annotations.

This will become an error in Kotlin 2.1.
w: <path_to>/Main.kt:17:20 This 'copy()' exposes the non-public primary constructor of a 'data class'. Please migrate the usage. See the appropriate 'data class' documentation or contact the 'data class' author for migration guidance. This will become an error in Kotlin 2.1.

将来的にcopyメソッドの可視性が変更されることを説明しています。

アノテーションを使ったOpt In

@ConsistentCopyVisibility を使うことで警告ではなくエラーにすることができます。

@ConsistentCopyVisibility // <- Opt-In
data class User private constructor(
val name: String,
val age: Int
) {
// ...
}

このアノテーションをつけたdata classではcopyメソッドがprivateになるため、ビルドエラーになります。

compiler flagでのOpt In

すべてdata classでOpt inしたい場合はcompiler flagを設定することで、対応できます。 -Xconsistent-data-class-copy-visibility を使います。

tasks.named("compileKotlin", org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask::class.java) {
compilerOptions {
freeCompilerArgs.add("-Xconsistent-data-class-copy-visibility")
}
}

逆にcompiler flagでOpt inした場合に特定のdata classは除外したい場合は、 @ExposedCopyVisibility をつけると除外できます。

@ExposedCopyVisibility // <- copyメソッドが使えるようになる
data class User private constructor(
val name: String,
val age: Int
) {
// ...
}

今後

https://youtrack.jetbrains.com/issue/KT-11914

こちらのYouTrackに記載されています。

  • 2.0.20では警告が表示。compiler flag等でエラーにすることができる。
  • 2.1 or 2.2では警告がエラーになりますが、バイナリは互換性の問題からpublicで生成される。
  • 2.2 or 2.3ではエラーになる。compiler flag等は不要になる。

--

--

Kenji Abe

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