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
インターフェースNamedのname: StringがPersonで実装されていなければ、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()関数の両方に当てはまる。