はじめに
protectedを利用したい場合には抽象クラスを使えば良いですが、すでに他のクラスを継承していて継承できないとか事情がある場合にはinterfaceでpublic/privateで定義をするしかありません。
でもinterfaceを実装したクラス以外から呼ばれたくない(呼ぶ必要がないから隠したい)ということも稀にあるかなと思います。
ひとまず、Kotlinのinterfaceというとだいたい下記のようになるかと思います。
interface SampleInterface {
//サブクラス以外から見られたくない
val sampleText: String
//サブクラス以外から呼ばれてもOK
fun callSampleMethod(){
sampleMethod()
}
//privateなら指定できる
private fun sampleMethod(){
//何らかの処理
}
}
Kotlinのinterfaceでは実装を持つことができるので抽象クラスとの違いがわかりにくくなった感があります。
ついでにprivateも従来のJavaのinterfaceクラスでは定義できませんでした。
Java8でできるようになったんだったか...
そしてinterfaceを実装したクラスというのが下記のコードです。
class Sample1(): SampleInterface {
override val sampleText: String
get() = "hoge"
}
ただし、これだと外部から参照できる必要のないSample1().sampleText
が参照できてしまいます。
委譲を利用したアダプターパターン(ラッパーパターン)を利用する
下記のSample2クラスはSampleInterfaceのinstanceを持つクラスです。
class Sample2() {
private val sample = object: SampleInterface {
override val sampleText: String
get() = "huga"
}
fun callMethod() {
sample.callSampleMethod()
}
}
SampleInterfaceのinstanceをprivateに持つことでSampleInterfaceそのものを外部から参照出来なくし、擬似的にprotectedな状態を再現しています。
ちなみにprivate val sample = Sample1()
でも良いですし、その方が良いパターンもあると思います。
ただ、SampleInterface内のpublicな変数・関数が全て擬似protectedになってしまうので意図に沿った実装になるか注意が必要です。
まとめ
Sample1、Sample2それぞれの変数・関数の呼び出しは下記の通りです。
val sample1 = Sample1()
sample1.sampleText //見えちゃう
sample1.callSampleMethod()
val sample2 = Sample2()
sample2.sampleText //見えない
sample2.callMethod()//callSampleMethodを呼び出す関数
ちなみに、当然ですが委譲を利用したアダプターパターン(ラッパーパターン)なので抽象クラスでもオブジェクトでも同じように実装できます。
感想
委譲するか実装を持つようにするかは個人の好みでも良いのかもしれません。