1概要

このチュートリアルでは、Kotlinでインターフェイスを定義して実装する方法について説明します。

また、クラスによって複数のインターフェイスを実装する方法についても説明します。これは確かに衝突を引き起こす可能性があります、そして我々はKotlinがそれらを解決しなければならないメカニズムを学びます。


2 Kotlin

のインターフェース

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

Kotlinのインターフェースは、Javaのような他の多くの言語のインターフェースと似ています。しかし、それらは特定の構文を持っています。次のいくつかのサブセクションでそれらを見てみましょう。


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

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

interface SimpleInterface

これは完全に空の最も単純なインターフェースです。

これらはマーカーインタフェースとも呼ばれます

それでは、インターフェイスにいくつかの機能を追加しましょう。

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

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

  • そのうちの1つはf

    __irstMethod

    __は抽象メソッドです

  • s

    __econdMethod

    __というもう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

    も文字列型ですが、

そのアクセサの実装を定義します。

インタフェースの

プロパティは状態

を維持できないことに注意してください。したがって、以下は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)
    }
}


SimpleClass



SimpleInterface

の実装として定義するときは、抽象プロパティと抽象関数の実装を提供するだけでよいことに注意してください。ただし、** 以前に定義されたプロパティまたは関数もオーバーライドできます。

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

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委譲によるインタフェースの実装

委譲は、継承ではなく合成によってコードの再利用性を実現するためのオブジェクト指向プログラミングの設計パターンです。

これはJavaのような多くの言語で実装することが可能ですが、

Kotlinは委任

による実装をネイティブでサポートしています** 。

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

interface MyInterface {
    fun someMethod(): String
}

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

これまでのところ、何も新しいことはありません。しかし今では、


_ MyInterface

_

through委任を実装する別のクラスを定義できます。

class MyDerivedClass(myInterface: MyInterface) : MyInterface by myInterface


MyDerivedClass

は、インターフェース

MyInterface

を実際に実装する引数としてデリゲートを期待します。

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

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

ここでは

MyClass

をインスタンス化し、__MyDerivedClass上のインタフェースの関数を呼び出すためのデリゲートとしてそれを使用しました。


3多重継承

多重継承は、オブジェクト指向プログラミングパラダイムにおける重要な概念です。 ** これにより、クラスはインタフェースのように複数の親オブジェクトから特性を継承することができます。

これによりオブジェクトモデリングの柔軟性が高まりますが、独自の複雑さがあります。そのようなものの1つが「ダイヤモンド問題」です。


Java 8

には、ダイヤモンドの問題に対処するための独自のメカニズムがあります。複数の継承が可能な他の言語と同様です。

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")
    }
}

ご覧のとおり、

SomeClass



FirstInterface



SecondInterface

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


3.2. 衝突を解決する

複数のインタフェースを実装するとき、クラスは複数のインタフェースの同じコントラクトに対してデフォルトの実装を持つ関数を継承するかもしれません。このため、実装クラスのインスタンスからこの関数を呼び出すという問題が発生します。

  • この矛盾を解決するために、Kotlinはサブクラスにそのような関数のオーバーライドされた実装を提供して解決を明示的にすることを要求します** 。

たとえば、上記の

SomeClass



anotherMethod

を実装しています。しかし、それができなかった場合、Kotlinは、最初の

または

Interfaceの

anotherMethod

のデフォルト実装を呼び出すかどうかわからないでしょう。このため、


SomeClass


** は

__anotherMethod

__を実装する必要があります。

ただし、実際には競合がないため、

__omeMethod


は少し異なります。


FirstInterfaceは、

someMethod

のデフォルト実装を提供していません。とは言っても、

SomeClass__は、

Kotlinがすべての継承された関数を、親インタフェースで1回定義されているか複数回定義されているかにかかわらず

実装することを強制するため、依然として実装する必要があります。


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<SecondChildInterface>.someMethod()
    }
}

ここでは、

someMethod

という抽象関数を宣言した

BaseInterface

を定義しました。インタフェース

FirstChildInterface



SecondChildInterface

はどちらも

BaseInterface

を継承し、関数

someMethod

を実装しています。


FirstChildInterface



SecondChildInterface

から継承した

ChildClass

を実装したので、関数

someMethod

をオーバーライドする必要があります。ただし、** メソッドをオーバーライドする必要がある場合でも、

SecondChildInterface

を使用してここで行うように、単に

super

を呼び出すことができます。


4 Kotlin

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

Kotlinの抽象クラスは、インスタンス化できない** クラスです。

これには、1つ以上のプロパティと関数を含めることができます。これらの特性および機能は抽象的または具体的であり得る。抽象クラスから継承するクラスは、そのクラス自体も抽象として宣言されていない限り、継承された抽象プロパティおよび関数をすべて実装する必要があります。


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

待つ!その音は、インターフェイスの動作とまったく同じではありませんか

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

  • Kotlinのクラスは好きなだけインターフェイスを実装できますが

1つの抽象クラスからしか拡張できない
** インターフェイス内のプロパティは状態を維持できませんが、状態は維持できます。

抽象クラス


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

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

通常、インタフェースを使用してコントラクトを定義する必要があります。これによって、約束した機能が引き出されます。実装クラスはそれらの約束を果たす責任を持ちます。ただし、抽象クラスは、拡張クラスと部分的な特性を共有するために使用する必要があります。拡張クラスはそれを完了するためにさらにそれを取ることができます。


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

Java 8でのJavaインターフェースへの変更により、それらはKotlinインターフェースに非常に近くなりました。以前の記事の1つに、https://www.baeldung.com/java-8-new-features[Java 8で導入された新機能(インターフェースの変更を含む)]が取り込まれています。

現在、JavaとKotlinのインターフェースには構文上の違いがほとんどあります。際立っている1つの違いは、キーワード「オーバーライド」に関連しています。 Kotlinでは、インターフェイスから継承した抽象プロパティまたは関数を実装する際に、キーワード「

override

」でそれらを修飾することが必須です。 Javaにはそのような明示的な要件はありません。


6. 結論

このチュートリアルでは、Kotlinインターフェース、それらをどのように定義して実装するかについて議論しました。それから我々は複数のインターフェースから継承することとそれらが作り出すかもしれない衝突について話した。 Kotlinがそのような衝突をどのように処理するかを見ました。

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

また、KotlinインターフェースとJavaインターフェースとの違いについても簡単に説明しました。

いつものように、例のコードはhttps://github.com/eugenp/tutorials/tree/master/core-kotlin[GitHubで利用可能]です。