Kotlin 接口关键字
最后修改于 2025 年 4 月 19 日
Kotlin 接口定义了类可以实现的契约。它们可以包含抽象方法和属性,以及默认实现。接口支持 Kotlin 中的多态性和多重继承。本教程通过实际示例深入探讨了 interface 关键字。
基本定义
Kotlin 中的接口是类的蓝图。它包含抽象方法声明和方法实现。接口不能存储状态,但可以拥有属性。类使用 : 语法实现接口。一个类可以实现多个接口。
基本接口声明
最简单的接口包含抽象方法声明。实现接口的类必须为这些方法提供具体的实现。这强制执行了一个契约,实现类必须遵守该契约。
package com.zetcode
interface Greeter {
fun greet(): String
}
class Person : Greeter {
override fun greet(): String {
return "Hello!"
}
}
fun main() {
val person = Person()
println(person.greet()) // Output: Hello!
}
在这里,我们定义了一个带有抽象方法的 Greeter 接口。Person 类实现了这个接口,并提供了方法体。实现接口方法时需要 override 关键字。
具有默认实现的接口
Kotlin 接口可以为方法提供默认实现。实现接口的类可以使用这些默认值或覆盖它们。在某些情况下,此功能使接口比抽象类更灵活。
package com.zetcode
interface Vehicle {
fun start() {
println("Vehicle started")
}
fun stop()
}
class Car : Vehicle {
override fun stop() {
println("Car stopped")
}
}
fun main() {
val car = Car()
car.start() // Output: Vehicle started
car.stop() // Output: Car stopped
}
Vehicle 接口为 start 提供了默认实现,但将 stop 保持为抽象。Car 类只需要实现 stop 并继承默认的 start 行为。
接口属性
接口可以声明实现类必须提供的抽象属性。这些属性可以实现为字段或通过 getter。接口属性不能有后备字段,但可以有自定义访问器。
package com.zetcode
interface User {
val name: String
val age: Int
get() = 0 // Default implementation
}
class RegisteredUser(override val name: String) : User {
// age uses default implementation
}
fun main() {
val user = RegisteredUser("John Doe")
println("${user.name}, ${user.age}") // Output: John Doe, 0
}
User 接口声明了两个属性。name 是抽象的,必须实现,而 age 具有默认值。RegisteredUser 类将 name 实现为属性并使用默认的 age 实现。
多接口实现
Kotlin 支持多接口继承。一个类可以实现多个接口,继承它们的所有成员。如果存在方法冲突,实现类必须显式解决它们。
package com.zetcode
interface Flyable {
fun fly() {
println("Flying high")
}
}
interface Swimmable {
fun swim() {
println("Swimming deep")
}
}
class Duck : Flyable, Swimmable {
fun describe() {
fly()
swim()
}
}
fun main() {
val duck = Duck()
duck.describe()
// Output:
// Flying high
// Swimming deep
}
Duck 类同时实现了 Flyable 和 Swimmable 接口。它可以调用这两个接口的方法。这演示了 Kotlin 通过接口对行为的多重继承的支持。
解决方法冲突
当多个接口声明了具有相同签名的方法时,实现类必须解决冲突。这是通过覆盖该方法并指定要使用哪个接口的实现来完成的。
package com.zetcode
interface A {
fun foo() {
println("A's foo")
}
}
interface B {
fun foo() {
println("B's foo")
}
}
class C : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
println("C's foo")
}
}
fun main() {
val c = C()
c.foo()
// Output:
// A's foo
// B's foo
// C's foo
}
C 类实现了同时具有冲突 foo 方法的 A 和 B。该类通过使用 super<Interface> 语法调用两个实现并添加其自身行为来解决此问题。
函数式接口 (SAM 接口)
单抽象方法 (SAM) 接口可以使用 lambda 实现。这些在 Kotlin 中被称为函数式接口。fun 修饰符将接口标记为函数式接口。
package com.zetcode
fun interface StringProcessor {
fun process(input: String): String
}
fun main() {
val upperCaseProcessor = StringProcessor { it.uppercase() }
val lowerCaseProcessor = StringProcessor { it.lowercase() }
println(upperCaseProcessor.process("Hello")) // Output: HELLO
println(lowerCaseProcessor.process("World")) // Output: world
}
StringProcessor 接口使用 fun 标记,使其成为函数式接口。我们使用 lambda 创建实例,而不是完整的类实现。这为简单的接口实现提供了简洁的语法。
接口继承
接口可以从其他接口继承,形成接口层次结构。子接口包括其父接口的所有成员。实现类必须满足整个层次结构的所有要求。
package com.zetcode
interface Animal {
fun eat()
}
interface Mammal : Animal {
fun nurse()
}
class Human : Mammal {
override fun eat() {
println("Eating food")
}
override fun nurse() {
println("Nursing young")
}
}
fun main() {
val human = Human()
human.eat() // Output: Eating food
human.nurse() // Output: Nursing young
}
Mammal 接口扩展了 Animal,继承了它的 eat 方法。Human 类实现了 Mammal,并且必须为 eat 和 nurse 提供实现。这展示了接口层次结构在 Kotlin 中的工作方式。
接口的最佳实践
- 优先使用接口而不是抽象类: 尽可能使用接口来定义契约,因为它们支持多重继承。
- 明智地使用默认实现: 为在不同实现之间具有通用行为的方法提供默认实现。
- 保持接口专注: 遵循接口隔离原则 - 接口应该小而专注。
- 文档接口契约: 清楚地记录实现者必须提供的内容以及调用者可以期望的内容。
- 考虑函数式接口: 当单个抽象方法足以满足契约时,使用 SAM 接口。
来源
本教程深入探讨了 Kotlin 的 interface 关键字,展示了基本声明、默认方法、属性和多重继承。我们探讨了各种场景,包括方法冲突和函数式接口。正确使用接口可以使您的代码更灵活、更易于维护。
作者
列出 所有 Kotlin 教程。