5.8.4 接口

原文链接: https://kotlinlang.org/docs/interfaces.html

5.8.4 接口

Kotlin 中的接口既可以包含抽象方法的声明,也可以包含方法的实现。它们与抽象类的不同之处在于:接口不能保存状态。接口可以有属性,但这些属性必须是抽象的,或者提供访问器的实现。

接口使用关键字 interface 定义:

1
2
3
4
5
6
interface MyInterface {
    fun bar()
    fun foo() {
      // 可选的函数体
    }
}

实现接口

一个类或对象可以实现一个或多个接口:

1
2
3
4
5
class Child : MyInterface {
    override fun bar() {
        // 函数体
    }
}

接口中的属性

你可以在接口中声明属性。接口中声明的属性既可以是抽象属性,也可以为访问器提供实现。接口中声明的属性不能有幕后字段,因此接口中声明的访问器不能引用它们:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
interface MyInterface {
    val prop: Int // 抽象

    val propertyWithImplementation: String
        get() = "foo"

    fun foo() {
        print(prop)
    }
}

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

接口继承

接口可以派生自其他接口,也就是说它既可以为其成员提供实现,也可以声明新的函数和属性。很自然,实现这类接口的类只需要定义缺失的实现即可:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
interface Named {
    val name: String
}

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

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

data class Employee(
    // 不需要实现 'name'
    override val firstName: String,
    override val lastName: String,
    val position: Position
) : Person

解析重写冲突

当你在超类型列表中声明许多类型时,可能会继承同一个方法的多个实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
interface A {
    fun foo() { print("A") }
    fun bar()
}

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

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

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

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

接口 A 和 B 都声明了函数 foo() 和 bar()。它们都实现了 foo(),但只有 B 实现了 bar()(bar() 在 A 中未标记为抽象,因为函数没有函数体时这正是接口的默认情况)。现在,如果你从 A 派生出一个具体类 C,就必须重写 bar() 并提供实现。

然而,如果你从 A 和 B 派生出 D,则需要实现从多个接口继承来的所有方法,并指明 D 究竟应该如何实现它们。这条规则既适用于只继承了单个实现的方法(bar()),也适用于继承了多个实现的方法(foo())。

接口函数的 JVM 默认方法生成

在 JVM 上,接口中声明的函数会被编译为默认方法。你可以用 -jvm-default 编译器选项控制这一行为,它有以下取值:

  • enable(默认):在接口中生成默认实现,并在子类和 DefaultImpls 类中包含桥接函数。使用此模式可以保持与旧版 Kotlin 的二进制兼容性。
  • no-compatibility:只在接口中生成默认实现。此模式会跳过兼容性桥接和 DefaultImpls 类,适合新的 Kotlin 代码。
  • disable:跳过默认方法,只生成兼容性桥接和 DefaultImpls 类。

要配置 -jvm-default 编译器选项,请在 Gradle Kotlin DSL 中设置 jvmDefault 属性:

1
2
3
4
5
kotlin {
    compilerOptions {
        jvmDefault = JvmDefaultMode.NO_COMPATIBILITY
    }
}