昨天介紹了繼承,它讓我們可以制定一個較為通用的父類別,透過子類別去繼承父類別並對父類別的變數與函式進行修改或是額外新增專屬子類別的變數與函式去描繪現實世界。但這樣的做法變相要求工程師必須要先將父類別中的所有函式與變數,另外對於繼承的行為上也只限於繼承一個父類別。為此,物件導向語言常提供介面 (interface
) 的寫法去突破類別繼承的限制。
在Kotlin中,介面可以包含抽象函式(沒有具體實作的函式)以及具體函式(有具體實作)。類別可以實作(implement)介面,可以想像成繼承概念,但因為介面本身並不是一個類別,所以我們會用 “實作” 去描述將介面擴展成類別的過程。這裡我們整理一些介面之餘類別的優勢
在 Kotlin 中,我們使用 interface
搭配介面名稱去宣告一個介面,介面可以包含抽象函式,這些函式沒有具體實作。實作介面的類別必須為這些方法提供具體實作。例如:
interface MyInterface {
fun doSomething() // 抽象方法
}
介面也可以包含抽象變數,這些屬性沒有初始值:
interface MyInterface {
val property: Int // 抽象屬性
}
介面可以包含方法實作(具體函式)。這些函式在介面中實作,提供預設行為。實作介面的類別可以選擇覆寫這些方法或使用預設實作:
interface MyInterface {
fun doSomething() {
println("在介面中做些事情")
}
}
類別可以使用:
符號,後接介面名稱,實作一個或多個介面:
class MyClass : MyInterface {
override fun doSomething() {
println("在類別中做些事情")
}
}
如果一個類別實作了具有相同名稱方法的多個介面,則必須有另外處理:
interface InterfaceA {
fun commonMethod() {
println("InterfaceA commonMethod()")
}
}
interface InterfaceB {
fun commonMethod() {
println("InterfaceB commonMethod()")
}
}
// 這會會出現編譯錯誤。
class MyClass : InterfaceA, InterfaceB {
// do something
}
我們必須將其修改為直接告訴編譯器我們期望怎麼做:
class MyClass : InterfaceA, InterfaceB {
override fun commonMethod() {
println("Implemented commonMethod()")
}
}
fun main() {
val myClass = MyClass()
myClass.commonMethod() // 輸出: Implemented commonMethod()
}
或也可以指定要從哪個介面去實作:
class MyClass : InterfaceA, InterfaceB {
override fun commonMethod() {
// 呼叫 InterfaceA 的 commonMethod()
super<InterfaceA>.commonMethod()
}
}
fun main() {
val myClass = MyClass()
myClass.commonMethod() // 輸出: InterfaceA commonMethod()
}
相似於介面函式實作過程,如果介面有提供就必須實作。
class MyClass : MyInterface {
override val property: Int = 42
}
除了類別可以繼承類別、類別可以實作介面外,其實介面也是可以繼承介面的?讓我們看一個範例:
interface Named {
val name: String
}
interface Person : Named {
val firstName: String
val lastName: String
override val name: String get() = "$firstName $lastName"
}
class Employee(
firstName: String,
lastName: String,
position: String
) : Person {
override val firstName: String = firstName
override val lastName: String = lastName
val position: String = position
fun greeting(): String {
return name
}
}
fun main() {
val member = Employee("John", "Wu", "Engineer")
println(member.greeting()) // John Wu
}
上述範例先定義了一個介面 Named
,然後由 Person
介面繼承 Named
介面,並透過本身有兩個變數 firstName
和 lastName
組成 name
。這個行為介面覆寫了 Named
介面的 name
變數,使得最後可以在 Employee
類別的物件中直接訪問複寫的 name
變數。