Kotlin 介面 (Interface)
介面 (Interface) 用於定義一組類別應該遵守的行為規範 (Contract)。從 Kotlin 1.0 開始,介面不僅可以包含抽象方法的宣告,還可以包含實作的方法 (Default Implementation) 和抽象屬性。
這使得 Kotlin 的介面比早期的 Java 介面更加強大。
定義介面
使用 interface 關鍵字來定義:
interface MyInterface {
// 抽象方法 (沒有實作本體)
fun bar()
// 擁有預設實作的方法 (Default Implementation)
fun foo() {
println("foo")
}
}
實作介面
類別或物件可以使用冒號 : 來實作介面。這與繼承類別的語法相同。
如果一個類別實作了某個介面,它必須覆寫 (Override) 該介面中所有的抽象成員。
class Child : MyInterface {
override fun bar() {
// 必須實作抽象方法
println("bar")
}
// foo() 已經有預設實作,所以是選用的 (Optional),不一定要覆寫
}
fun main() {
val c = Child()
c.foo() // 輸出 foo
c.bar() // 輸出 bar
}
介面中的屬性 (Properties in Interfaces)
你可以在介面中定義屬性。這些屬性可以是抽象的 (abstract),也可以有自定義的存取器 (Custom Accessor)。 注意:介面中的屬性不能有 Backing Field (不能直接賦值狀態)。
interface Named {
// 抽象屬性,實作類別必須覆寫它
val name: String
// 具有自定義 Getter 的屬性 (這不是抽象屬性,可以不覆寫)
val description: String
get() = "My name is $name"
}
class Person(override val name: String) : Named
fun main() {
val p = Person("Mike")
println(p.description) // 輸出: My name is Mike
}
介面繼承 (Interface Inheritance)
介面也可以繼承其他介面,這樣可以擴充新的方法或屬性,或者為繼承來的屬性提供預設實作。
interface Named {
val name: String
}
interface Person : Named {
val firstName: String
val lastName: String
// 在介面中實作了父介面的屬性
override val name: String
get() = "$firstName $lastName"
}
解決覆寫衝突 (Resolving Overriding Conflicts)
當一個類別實作了多個介面,而這些介面剛好有同名且都有實作的方法時,會發生衝突。 這時,編譯器會強制要求你在類別中覆寫該方法,並明確指定要呼叫哪一個父介面的實作。
使用 super<介面名稱>.方法名() 來指定呼叫對象。
interface A {
fun foo() { print("A") }
fun bar() // 抽象方法
}
interface B {
fun foo() { print("B") }
fun bar() { print("Bar") }
}
class C : A, B {
override fun foo() {
// 當 A 和 B 都有 foo() 實作時,必須明確指定
super<A>.foo()
super<B>.foo()
}
override fun bar() {
// 雖然 B 有 bar() 的實作,但 A 的 bar() 是抽象的
// 只要並非全部都是預設實作,通常都需要明確處理或是直接覆寫
super<B>.bar()
}
}
函數式介面 (Functional Interface / SAM)
如果一個介面只有一個抽象方法,它被稱為函數式介面 (Functional Interface) 或 SAM (Single Abstract Method) 介面。
可以加上 fun 關鍵字來宣告,這樣可以使用 Lambda 表達式來簡化實作。
fun interface IntPredicate {
fun accept(i: Int): Boolean
}
fun main() {
// 不用寫 object : IntPredicate { ... },直接用 Lambda
val isEven = IntPredicate { it % 2 == 0 }
println(isEven.accept(5)) // false
}