Upgrade to Pro — share decks privately, control downloads, hide ads and more …

copy メソッドが壊す data class のドメイン制約

copy メソッドが壊す data class のドメイン制約

Avatar for Tomoyuki TAKEZAKI

Tomoyuki TAKEZAKI

May 25, 2025
Tweet

More Decks by Tomoyuki TAKEZAKI

Other Decks in Programming

Transcript

  1. 今日話すこと 問題 Kotlin 2.0.20 以前のバージョンでは、data class の copy メソッドが public

    である。 結果として、data class の copy メソッドを使うと、ドメイン制約を満たさないような任意の インスタンスが生成できてしまう。 解決策 Kotlin のバージョンアップに追随する。変更のタイムラインについては後述する。 2
  2. ドメイン制約を満たさない実装の例 data class Ticket(val id: Uuid, val isAvailable: Boolean) この実装は、明らかにドメイン制約を満たさない。初期状態が利用不能になっているチケッ

    トを作成できてしまう。 val invalid = Ticket(id = Uuid.random(), isAvailable = false) 次のような改善が必要である。 primary constructor を制限し、 isAvailable が true のインスタンスのみを生成で きるようにする isAvailable が true の場合にのみ、同一 id の消費済みのチケットのインスタン スが作成できるようにする 4
  3. ドメイン制約を満たすはずの実装例 data class Ticket private constructor( val id: Uuid, val

    isAvailable: Boolean, ) { companion object { fun issue(): Ticket = Ticket(id = Uuid.random(), isAvailable = true) } fun consume(): Ticket = if (isAvailable) { this.copy(isAvailable = false) } else { throw IllegalStateException("Ticket is already used") } } きちんとドメイン制約を満たす実装に見えるが、果たして本当にそうだろうか? 5
  4. copy メソッドによる不正なインスタンスの作成 Ticket クラスがドメイン制約を本当に満たすかは、 Kotlin のバージョンに依存する。 なぜなら Kotlin 2.0.20 より前のバージョンでは、copy

    メソッドは常に public であり、 copy メソッドを利用して不正なインスタンスを生成することが常に可能だからである。 val used = Ticket.issue().consume() // 使用済みチケットの状態を改変できるが、これはドメイン制約を満たさない val falsified = used.copy(isAvailable = true) println(falsified) // Ticket(id=..., isAvailable=true) copy メソッドを利用してドメイン制約を満たさないインスタンスを作成できてしまう。 6
  5. 変更のタイムライン(Kotlin 公式サイトより引用) 原稿執筆時点の最新の安定版 2.1.21 では phase 1 の状態です Phase 1.

    Kotlin 2.0.20. The compiler warns about the behavior change on the data class declaration and on illegal copy method usages (illegal usages are those that will become invisible by the end of the migration).... Phase 2. (Supposedly Kotlin 2.1 or Kotlin 2.2). The warnings turn into errors. Keep in mind that the compiler still generates public copy under the hood.... Phase 3. (Supposedly Kotlin 2.2 or Kotlin 2.3). The default changes. Unless ExposedCopyVisibility is used, the generated copy method has the same visibility as the primary constructor.... 出典: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-consistent-copy-visibility/ 7
  6. まとめと推奨アクション Kotlin < 2.0.20 を使っている場合 コンストラクタ制限だけでなく、copy の存在を意識した設計にする Kotlin 2.0.20 以降にアップデートする場合

    ConsistentCopyVisibility に関する仕様を理解しておく コンパイラ警告が出た箇所は積極的に見直す いずれの場合も、copy メソッドによるドメイン違反の可能性をコードレビューで確認す る。 8