ProgrammingKotlin開発者

Kotlinにおけるデータオブジェクト(data object)はどのように機能し、何のために必要で、equals/hashCode/toStringがどのように実装されているのか、通常のオブジェクトやデータクラスとどのように異なるのか?

Hintsage AIアシスタントで面接を突破

回答。

問題の歴史:

Kotlin 1.9以前では、オブジェクトがデータオブジェクトになることはできませんでした。シングルトンで自動的にequals、hashCode、toStringを取得することはできず、データクラスのようにはいきませんでした。data objectの登場でこの制限は解除されました。これにより、値のパターンや列挙型のような用途に適した自動生成メソッドを持つシングルトンオブジェクトを作成できるようになりました。

問題:

以前は、シングルトンオブジェクトの正しいequals()、hashCode()、toString()を取得するために手動で実装するか、他のトリックを使用しなければならず、ボイラープレートが増え、エラーの可能性が高まりました。

解決策:

一意のインスタンスを持つオブジェクトにはデータオブジェクトを使用し、コレクションに渡すため、シリアライズ、比較、デバッグのために標準的なequals/hashCode/toStringの動作が必要です。

コード例:

data object NotAvailable fun checkStatus(status: Any) = when (status) { NotAvailable -> "データがありません" else -> "他のステータス" } val set = setOf(NotAvailable) println(NotAvailable in set) // true println(NotAvailable.toString()) // NotAvailable

主な特徴:

  • データオブジェクトはデータクラスの契約に従ってequals/hashCode/toStringを実装します。
  • これはシングルトンオブジェクト(唯一のインスタンス)です。
  • データなしの値のようなパターンや列挙型のようなパターンに適しています。

トリッキーな質問。

データオブジェクトはプロパティを含むことができますか?

はい、valプロパティのみが可能で、バックフィールドはありません(シングルトンに保存されるものはないため)。

data object Loading { val status: String get() = "読み込み中..." }

データオブジェクトは通常のオブジェクトとequalsの面で何が違いますか?

通常のオブジェクトのequalsは参照の同一性をチェックしますが、データオブジェクトはデータの契約を比較します。ただし、シングルトンの場合、常に同じオブジェクトです。とはいえ、オーバーライドされたequals/hashCodeはコレクションにとっては有用です。

データオブジェクトから継承することはできますか?

いいえ、データオブジェクトはファイナルで、Kotlinの通常のオブジェクトと同様に継承はできません。

よくある間違いやアンチパターン

  • 複数のオブジェクトがある場合にenumの代わりにデータオブジェクトを使用すること
  • データを保存しようとしてデータオブジェクトを使用すること—許可されていません。
  • 等価性が常に参照に基づくが、コレクションによっては値に基づくことを考慮しないこと。

実生活の例

ネガティブケース

すべての状態に対してenumの代わりに異なるデータオブジェクトを使用しました。1年後、文字列によるシリアライズが必要になり、オブジェクト名とタイプを手動で対応付けることになりました。

利点:

  • 簡単な初期化

欠点:

  • シリアライズのための余分なコスト、マッピングのエラー

ポジティブケース

ネットワークリクエストの戻り値として、特別なステータスのためにデータオブジェクトを使用しました: Loading, Empty, Error。これによりコードはコンパクトで、equals、hashCode、toStringが自動的にサポートされます。

利点:

  • コレクション内での確認に便利
  • 美しいロギング

欠点:

  • 変数プロパティを追加できない、val-lazyのみ