Kotlinインターフェースの手引き

Kotlinインターフェイスのガイド

1. 概要

このチュートリアルでは、we’ll discuss how to define and implement interfaces in Kotlin.

また、クラスによって複数のインターフェースを実装する方法についても見ていきます。 これは確かに競合を引き起こす可能性があり、Kotlinがそれらを解決する必要があるメカニズムを学習します。

2. Kotlinのインターフェース

インターフェイスは、オブジェクト指向プログラミングでクラスの説明またはコントラクトを提供する方法です。 プログラミング言語に応じて、抽象的または具体的な方法でプロパティと機能を含めることができます。 Kotlinのインターフェースの詳細について説明します。

Kotlinのインターフェースは、Javaのような他の多くの言語のインターフェースに似ています。 ただし、それらには特定の構文があります。次のいくつかのサブセクションでそれらを確認しましょう。

2.1. インターフェイスの定義

Kotlinで最初のインターフェースを定義することから始めましょう:

interface SimpleInterface

これは、完全に空の最も単純なインターフェイスです。 These are also known as marker interfaces

次に、インターフェースにいくつかの関数を追加しましょう。

interface SimpleInterface {
    fun firstMethod(): String
    fun secondMethod(): String {
        return("Hello, World!")
    }
}

以前に定義したインターフェイスに2つのメソッドを追加しました。

  • firstMethod isと呼ばれるそれらの1つは抽象的なメソッドです

  • 一方、secondMethod と呼ばれるもう1つは、デフォルトの実装です。

さあ、インターフェースにいくつかのプロパティを追加しましょう。

interface SimpleInterface {
    val firstProp: String
    val secondProp: String
        get() = "Second Property"
    fun firstMethod(): String
    fun secondMethod(): String {
        return("Hello, from: " + secondProp)
    }
}

ここで、インターフェイスに2つのプロパティを追加しました。

  • それらの1つは、String型のfirstProp isと呼ばれ、抽象的です。

  • 2番目のsecondPropも文字列型ですが、アクセサーの実装を定義します。

properties in an interface cannot maintain stateに注意してください。 したがって、次はKotlinでの不正な表現です。

interface SimpleInterface {
    val firstProp: String = "First Property" // Illegal declaration
}

2.2. インターフェースの実装

基本的なインターフェースを定義したので、Kotlinのクラスにそれを実装する方法を見てみましょう。

class SimpleClass: SimpleInterface {
    override val firstProp: String = "First Property"
    override fun firstMethod(): String {
        return("Hello, from: " + firstProp)
    }
}

SimpleClassSimpleInterfacewe only have to provide the implementation for abstract properties and functionsの実装として定義する場合は注意してください。 ただし、can override any previously defined property or function, too.

クラスで以前に定義したすべてのプロパティと関数をオーバーライドしてみましょう。

class SimpleClass: SimpleInterface {
    override val firstProp: String = "First Property"
    override val secondProp: String
        get() = "Second Property, Overridden!"

    override fun firstMethod(): String {
        return("Hello, from: " + firstProp)
    }
    override fun secondMethod(): String {
        return("Hello, from: " + secondProp + firstProp)
    }
}

ここでは、以前にインターフェースSimpleInterfaceで定義されたプロパティsecondPropと関数secondFunctionをオーバーライドしました。

2.3 Implementing Interfaces through Delegation

委任は、オブジェクト指向プログラミングto achieve code reusability through composition instead of inheritanceのデザインパターンです。 これはJavaなどの多くの言語で実装できますが、Kotlin has native support for implementation through delegationです。

基本的なインターフェースとクラスから始める場合:

interface MyInterface {
    fun someMethod(): String
}

class MyClass() : MyInterface {
    override fun someMethod(): String {
        return("Hello, World!")
    }
}

これまでのところ、新しいものはありません。 しかし今、委任を通じてMyInterface を実装する別のクラスを定義できます。

class MyDerivedClass(myInterface: MyInterface) : MyInterface by myInterface

MyDerivedClassは、インターフェイスMyInterfaceを実際に実装する引数としてデリゲートを想定しています。

デリゲートを介してインターフェースの関数を呼び出す方法を見てみましょう。

val myClass = MyClass()
MyDerivedClass(myClass).someMethod()

ここでは、MyClassをインスタンス化し、実際にはこれらの関数を直接実装したことのないMyDerivedClass,でインターフェイスの関数を呼び出すためのデリゲートとして使用しました。

3. 多重継承

多重継承は、オブジェクト指向プログラミングのパラダイムにおける重要な概念です。 This allows for a class to inherit characteristics from more than one parent object, like an interface, for example

これにより、オブジェクトモデリングの柔軟性が向上しますが、独自の複雑なセットが伴います。 その1つが「ダイヤモンドの問題」です。

Java 8 has its own mechanisms for addressing the diamond problem、多重継承を可能にする他の言語と同様。

Kotlinがインターフェースを介してどのように対処するかを見てみましょう。

3.1. 複数のインターフェイスを継承する

まず、2つの単純なインターフェイスを定義します。

interface FirstInterface {
    fun someMethod(): String
    fun anotherMethod(): String {
        return("Hello, from anotherMethod in FirstInterface")
    }
}

interface SecondInterface {
    fun someMethod(): String {
        return("Hello, from someMethod in SecondInterface")
    }
    fun anotherMethod(): String {
        return("Hello, from anotherMethod in SecondInterface")
    }
}

両方のインターフェースに同じコントラクトを持つメソッドがあることに注意してください。

次に、これらの両方のインターフェイスから継承するクラスを定義しましょう。

class SomeClass: FirstInterface, SecondInterface {
    override fun someMethod(): String {
        return("Hello, from someMethod in SomeClass")
    }
    override fun anotherMethod(): String {
        return("Hello, from anotherMethod in SomeClass")
    }
}

ご覧のとおり、SomeClassFirstInterfaceSecondInterfaceの両方を実装しています。 構文的にはこれは非常に単純ですが、ここで注意が必要なセマンティクスが少しあります。 これについては、次のサブセクションで説明します。

3.2. 競合の解決

複数のインターフェイスを実装する場合、クラスは、複数のインターフェイスの同じコントラクトのデフォルト実装を持つ関数を継承できます。 これにより、実装クラスのインスタンスからこの関数を呼び出す問題が発生します。

To resolve this conflict, Kotlin requires the subclass to provide an overridden implementation for such functions to make the resolution explicit

たとえば、上記のSomeClassanotherMethodを実装します。 ただし、そうでない場合、KotlinはFirst またはSecondInterface’sのデフォルト実装のanotherMethodを呼び出すかどうかを知りません。 SomeClassmust implement anotherMethod for this reason.

ただし、実際には競合がないため、someMethod は少し異なります。 FirstInterface は、someMethodのデフォルトの実装を提供していません。 とはいえ、Kotlin forces us to implement all inherited functions, no matter if they are defined once or multiple times in parent interfacesはまだ実装する必要があるため、SomeClassはそれを実装する必要があります。

3.3. ダイヤモンド問題の解決

「ダイヤモンドの問題」は、ベースオブジェクトの2つの子オブジェクトが、ベースオブジェクトによって定義された特定の動作を記述するときに発生します。 ここで、これら両方の子オブジェクトから継承するオブジェクトは、サブスクライブする継承された動作を解決する必要があります。

この問題に対するKotlinの解決策は、前のサブセクションで多重継承に対して定義されたルールを使用することです。 ダイヤモンド問題を提示するために、いくつかのインターフェースと実装クラスを定義しましょう。

interface BaseInterface {
    fun someMethod(): String
}

interface FirstChildInterface: BaseInterface {
    override fun someMethod(): String {
        return("Hello, from someMethod in FirstChildInterface")
    }
}

interface SecondChildInterface: BaseInterface {
    override fun someMethod(): String {
        return("Hello, from someMethod in SecondChildInterface")
    }
}

class ChildClass: FirstChildInterface, SecondChildInterface {
    override fun someMethod(): String {
        return super.someMethod()
    }
}

ここでは、someMethodと呼ばれる抽象関数を宣言するBaseInterfaceを定義しました。 インターフェイスFirstChildInterfaceSecondChildInterfaceはどちらもBaseInterfaceを継承し、関数someMethodを実装します。

FirstChildInterfaceSecondChildInterfaceから継承するChildClassを実装するので、関数someMethodをオーバーライドする必要があります。 ただし、even though we must override the method, we can still simply call super as we do here with SecondChildInterface.

4. Kotlinの抽象クラスと比較したインターフェース

Kotlinの抽象クラスはclasses which cannot be instantiatedです。 これには、1つ以上のプロパティと関数が含まれる場合があります。 これらのプロパティと関数は、抽象的または具体的です。 抽象クラスから継承するクラスは、クラス自体も抽象として宣言されていない限り、継承されたすべての抽象プロパティと関数を実装する必要があります。

4.1. インターフェイスと抽象クラスの違い

待つ! それはインターフェースとまったく同じように聞こえませんか?

実際には、最初は、抽象クラスはインターフェイスとそれほど変わりません。 しかし、私たちの選択を左右する微妙な違いがあります。

  • Kotlinのクラスは、好きなだけ多くのインターフェイスを実装できますが、1つの抽象クラスからしか拡張できません

  • インターフェースのプロパティは、抽象クラスでは可能ですが、状態を維持できません

4.2. いつ何を使うべきですか?

インターフェースはクラスを定義するための青写真に過ぎず、オプションでデフォルトの実装をいくつか持つこともできます。 一方、抽象クラスは、拡張クラスによって完成される不完全な実装です。

通常、interfaces should be used to define the contractは、提供すると約束されている機能を引き出します。 実装クラスには、これらの約束を果たす責任があります。 abstract class, however, should be used to share partial characteristics with extending classes。 拡張クラスは、それをさらに処理して完了することができます。

5. Javaインターフェースとの比較

Java 8のJavaインターフェースへの変更に伴い、they have come very close to Kotlin interfaces。 以前の記事の1つは、the new features introduced in Java 8 including changes to the interfaceをキャプチャします。

現在、JavaとKotlinインターフェースの間にはほとんど構文上の違いがあります。 際立った違いの1つは、キーワード「オーバーライド」に関連しています。 Kotlinでは、インターフェイスから継承された抽象プロパティまたは関数を実装する際に、キーワード「override」でそれらを修飾する必要があります。 Javaにはそのような明示的な要件はありません。

6. 結論

このチュートリアルでは、Kotlinインターフェース、それらを定義および実装する方法について説明しました。 次に、複数のインターフェイスから継承することと、それらによって発生する可能性のある競合について説明しました。 Kotlinがこのような競合をどのように処理するかを調べました。

最後に、Kotlinの抽象クラスと比較したインターフェイスについて説明しました。 また、KotlinインターフェースとJavaインターフェースの比較についても簡単に説明しました。

いつものように、例のコードはover on GitHubで利用できます。