🦉
Kotlinのクラスの委譲で検討したいこと
はじめに
Kotlinの中で好きな機能はクラスの委譲(Delegation)です。
ただたまにハマることがあるので、最近気をつけているポイントを書いてみます。
解説
次のようなインタフェースとクラスがあります。
// インタフェース
interface Animal {
val name: String
val age: Int
// デフォルトメソッド
fun print() {
println("name=$name, age=$age")
}
}
// クラス
data class Dog(
override val name: String,
override val age: Int
) : Animal
// 委譲を使っているクラス
data class RenamedAnimal(
val animal: Animal,
override val name: String
): Animal by animal
次のコードを動かすと何が出力されるでしょうか?
fun main() {
val dog = Dog("taro", 10)
// 出力①
dog.print()
val renamed = RenamedAnimal( dog, "jiro")
// 出力②
renamed.print()
}
2回のprint
関数の呼び出しでは同じ結果が出力されます。
name=taro, age=10
name=taro, age=10
想像通りでしたでしょうか?
これ、仕様通りの当たり前の挙動ではあるんですが、個人的には次のように2回目の出力が変わることを期待してしまうことがよくあるんですよね。
name=taro, age=10
name=jiro, age=10
こんなとき、簡単に解決する方法があります。
それは、インタフェースに定義したデフォルトメソッドを拡張関数に変更することです。
interface Animal {
val name: String
val age: Int
}
// 拡張関数
fun Animal.print() {
println("name=$name, age=$age")
}
この修正をおこなった後で上述のmain
関数を動かすと2回目のprint
ではname=jiro, age=10
と出力されます。
まとめ
インタフェースにデフォルトの振る舞いがあるとき、メソッドとしてインタフェース内に置くのか、拡張関数としてインタフェースの外に置くのかは一考の価値があるかもしれません。
個人的にはデータと振る舞いを分けるのが好みなので最近はもっぱら拡張関数を使うようにしています。
Discussion