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
}