1. 概要

継承よりも委任が優先される多くのユースケースがあります。 Kotlinは、これを言語レベルでサポートしています。

このチュートリアルでは、委任パターンに対するKotlinのネイティブサポートについて説明し、実際の動作を確認します。

2. 実装

まず、サードパーティのライブラリに以下の構造のコード例があると仮定します。

interface Producer {

    fun produce(): String
}

class ProducerImpl : Producer {

    override fun produce() = "ProducerImpl"
}

次に、既存の実装 を「by」キーワードを使用して装飾し、必要な処理を追加します。

class EnhancedProducer(private val delegate: Producer) : Producer by delegate {

    override fun produce() = "${delegate.produce()} and EnhancedProducer"
}

したがって、この例では、EnhancedProducerクラスがタイプProducerdelegateオブジェクトをカプセル化することを示しています。 また、Producer実装の機能を使用することもできます。

最後に、期待どおりに機能することを確認しましょう。

val producer = EnhancedProducer(ProducerImpl())
assertThat(producer.produce()).isEqualTo("ProducerImpl and EnhancedProducer")

3. ユースケース

次に、委任パターンの2つの一般的なユースケースを見てみましょう。

まず、委任パターンを使用して、既存の実装を使用して複数のインターフェースを実装できます。

class CompositeService : UserService by UserServiceImpl(), MessageService by MessageServiceImpl()

次に、委任を使用して、既存の実装を拡張できます。

後者は、前のセクションで行ったことです。 ただし、以下のようなより現実的な例は、既存の実装を変更できない場合に特に役立ちます。たとえば、サードパーティのライブラリコードです。

class SynchronizedProducer(private val delegate: Producer) : Producer by delegate {

    private val lock = ReentrantLock()

    override fun produce(): String {
        lock.withLock { 
            return delegate.produce()
        }
    }
}

4. 委任は継承ではありません

ここで、デリゲートはデコレータについて何も知らないことを常に覚えておく必要があります。したがって、GoFテンプレートメソッドのようなアプローチを試してはいけません。

例を考えてみましょう:

interface Service {

    val seed: Int

    fun serve(action: (Int) -> Unit)
}

class ServiceImpl : Service {

    override val seed = 1

    override fun serve(action: (Int) -> Unit) {
        action(seed)
    }
}

class ServiceDecorator : Service by ServiceImpl() {
    override val seed = 2
}

ここで、デリゲート( ServiceImpl )は、共通インターフェースで定義されたプロパティを使用し、それをデコレーター( ServiceDecorator )でオーバーライドします。 ただし、デリゲートの処理には影響しません。

val service = ServiceDecorator()
service.serve {
    assertThat(it).isEqualTo(1)
}

最後に、Kotlinでは、インターフェースだけでなく、個別のプロパティにも委任できることに注意することが重要です。

5. 結論

このチュートリアルでは、Kotlinインターフェースの委任について、いつ使用するか、どのように構成するか、およびその注意事項について説明しました。

いつものように、この記事の完全なソースコードは、GitHubから入手できます。