Kotlin インターフェース

インターフェース(interface)

インターフェースは抽象クラスと同じく、それ自体をインスタンス化できず、実装すべきプロパティとメソッドを定義する。抽象クラスが「継承」のためのものだとすれば、インターフェースはサブクラスとしての「機能」を定義するもので、1つのサブクラスが複数のインターフェースを実装できる。

Kotlinのインターフェースには、抽象メソッドと実装を含めることができる。抽象クラスとの違いは、プロパティの状態情報を保存できない点である。

言い換えると、インターフェースではプロパティの初期化子(initializer)を指定できない。

interface User {
    val name = "devkuma" // Error: プロパティ初期化はインターフェースでは許可されない。
}

インターフェースはinterfaceキーワードを使って定義する。

interface MyInterface {
    fun bar()
    fun foo() {
      // optional body
    }
}

インターフェースの実装(Implementing Interfaces)

1つのクラスまたはオブジェクトは、1つ以上のインターフェースを持つことができる。インターフェースを継承した場合は、必ずインターフェースのメンバーを実装しなければならない。

class Child : MyInterface {
    override fun bar() {
        // body
    }
}

インターフェースのプロパティ(Properties in Interfaces)

インターフェースではプロパティを宣言できる。インターフェースに宣言されたプロパティは、抽象(abstract)であるか、アクセサ(accessor)の実装を持つ必要がある。インターフェースではプロパティを初期化できないため、インターフェースに宣言されたアクセサはバッキング状態を参照できない。

interface MyInterface {
    val prop: Int // abstract

    val propertyWithImplementation: String
        get() = "foo"

    fun foo() {
        print(prop)
    }
}

class Child : MyInterface {
    override val prop: Int = 29
}

インターフェースの継承(Interfaces Inheritance)

インターフェースは他のインターフェースを継承できる。そのため、継承するインターフェースでは上位インターフェースのメンバーを実装したり、新しいメソッドやプロパティを宣言したりできる。

このようなインターフェースを実装するクラスは、最終的に実装されていないメンバーだけを実装すればよい。

interface Named {
    val name: String
}

interface Person : Named {
    val firstName: String
    val lastName: String

    override val name: String get() = "$firstName $lastName"
}

data class Employee(
    // implementing 'name' is not required
    override val firstName: String,
    override val lastName: String,
    val position: Position
) : Person

インターフェースNamedname: StringPersonで実装されていなければ、Employeeで実装する必要がある。

オーバーライド競合の解決(Resolving overriding conflicts)

複数のインターフェースを継承して実装するとき、実装すべきメソッドが複数のインターフェースから継承される場合がある。

interface A {
    fun foo() { println("A") }
    fun bar() // abstract
}

interface B {
    fun foo() { println("B") }
    fun bar() { println("bar") }
}

class C : A {
    override fun bar() { println("bar") }
}

class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }

    override fun bar() {
        super<B>.bar()
    }
}

fun main() {
    val c = C()
    c.foo()
    c.bar()

    println()

    val d = D()
    d.foo()
    d.bar()
}

インターフェースAとBはどちらもfoo()bar()関数を宣言している。AとBの両方がfoo()を実装しており、Aはbar()を実装していないが、Bでは実装している。

クラスCはインターフェースAだけを継承しているため、bar()だけを実装すればよい。

クラスDはインターフェースAとBを継承しているため、重複するメソッドに対して明確な処理が必要である。これは、1回だけ実装されたbar()関数と、複数回実装されたfoo()関数の両方に当てはまる。

参考