Kotlin 線上讀書會 筆記 (十)介面Interface與抽象類別Abstract Class

Evan Chen
Evan Android Note
Published in
9 min readMay 22, 2020

介面 Interface

介面類別一樣可以定義方法,只是介面並不提供實作,方法成員由繼承介面的類別實作。而介面裡如果多個方法,繼承的類別每一個都必須實作。

//定義介面
interface MyInterface {
fun functionA()
}
//由類別繼承介面
class MyClass: MyInterface{
override fun functionA() {

}
}

一個類別可以繼承多個介面

//定義介面
interface MyInterface {
fun functionA()
}
interface MyInterface2 {
fun functionA2()
}
//由類別繼承介面
class MyClass: MyInterface, MyInterface2{
override fun functionA() {
} override fun functionA2() { }
}

來看範例:

有一個圓形矩形都有計算面積的功能,圓形跟矩形是兩個不同的類別,但有共同的行為「計算面積」。為了不讓兩個不同的類別使用繼承而打亂原有的架構,我們決定把這個共同的行為拉出來變為一個Interface,讓兩個不同類別都可以實作這個行為。

定義Interface及計算面積的方法area()

interface ISurfaceArea{
//計算面積
fun area() :Double
}

圓形類別繼承ISurfaceArea介面,實作area計算面積的方法

class Circle(private val radius:Double) : ISurfaceArea{
//實作計算面積
override fun area() :Double {
return radius * radius * 3.14
}
}

矩形類別繼承ISurfaceArea介面,實作area計算面積的方法

class Rectangle(private val width:Double, private val height:Double) : ISurfaceArea{
//實作計算面積
override fun area() :Double{
return width * height
}
}

這樣就完成了把共同的area計算面積提到Interface了。不管是圓形或是矩形,只要有需要計算面積,就可以繼承ISurfaceArea來實作計算面積。

用Interface實作Callback機制

Callback經常被用溝通及傳遞資料,Android就有大量的這種Interface。我們就從Callback的用法來更了解Interface。

View的Click

Button的點擊事件button.setOnClickListener就是一個Callback的用法。我們會在這裡實作這個Interface的onClick方法。

MainActivity.kt

button.setOnClickListener(object : View.OnClickListener{
override fun onClick(view: View?) {
//button點擊
}
})

如果我們點進去View.OnClickListener 可以看到定義如下。

public interface OnClickListener {
void onClick(View var1);
}

之所以需要這個Callback是因為當Button被點擊時,你會希望處理點擊事件的類別是Activity,而不是Button本身。透過Interface讓Button被點擊時交由另一個實作OnClickListener的類別實體去處理。所以不管是在Activity或Fragment只要你可以實作View.OnClickListener這個介面,你就可以處理Button的點擊事件。所以對Button點擊事件的處理來說,Button才不管你是哪個Class,反正你有實作這個Interface就好了。

看另一個例子AlertDialog

在點擊「取消」與「確認」時,同樣是透過DialogInterface.OnClickListener這個Interface來讓Activity實作。

AlertDialog.Builder(this).apply {
setTitle("標題")
setMessage("內容")
setNegativeButton("取消") { dialog, which ->
//點擊取消
}
setPositiveButton("確認") { dialog, which ->
//點擊確定
}
create()
show()
}

Fragment與Activity

Fragment與Activity需要溝通傳遞資料時,建議使用ViewModel。如果你不是用ViewModel的話,那麼也可以透過Interface來讓Fragment與Activity溝通。

class FragmentA : Fragment() {
//定義從Fragment返回Activity的Listener
interface OnChooseListener{
//在Fragment按下確定
fun onSure()

//在Fragment按下取消
fun onCancel()
}
internal var callback: OnChooseListener fun setOnChooseListener(callback: OnChooseListener) {
this.callback = callback
}
}
class MainActivity : Activity(), FragmentA.OnChooseListener {
// ...
fun onAttachFragment(fragment: Fragment) {
if (fragment is FragmentA) {
fragment.setOnChooseListener(this)
}
}
override fun onSure(){
//從Fragment點確定回來Activity
}
override fun onCancel(){
//從Fragment點取消回來Activity
}
}

再來看一個例子,有時候我們會把不同的元件組合為一個Custom Component,下圖的購物車購買份數選擇,就會把加減按鈕及中間的數字放在一個Custom Component。

當在寫這樣的Component時,大部分的行為會被封裝起來,對於使用這個Component的類別,Component被點擊了什麼,不是我在乎的,我們只在乎數字改變了,我們就會用定義一個Listener,用來通知使用這個Component的類別數字選擇器的數字改變了。

interface NumberSelectListener {
fun onValueChange(value: Int)
}

看完了這些在Android的Callback用法,對Interface是不是稍微有點了解了。

延申閱讀:

用Interface的MVP架構

用Interface來做依賴注入

抽象類別

定義一個抽象類別,需在class前面加上abstract,而抽象方法也需在fun前面加上abstract。

abstract class MyAbstractClass{
abstract fun functionA()
}

抽象類別不能直接被實例化,只能被類別繼承。

val myClass = MyAbstractClass() //Error

MyClass 繼承MyAbstractClass

class MyClass(): MyAbstractClass(){
override fun functionA() {

}
}

例如我們定義一個動物的抽象類別,裡面有一個抽象方法定義動物發出叫聲。

abstract class Animal{
abstract fun sound()
}

狗跟貓這兩個類別,繼承 Animal 抽象類別,就各自實作sound抽象方法。

class Dog(): Animal(){
override fun sound() {
//狗叫聲
}
}
class Cat(): Animal(){
override fun sound() {
//貓叫聲
}
}

接著看Android裡面有用到抽象類別的地方。Android 的RecyclerView裡的Adapter就用了抽象類別。

在使用RecyclerView時,就會使用到RecyclerView.Adpater。這個Adapter 就是一個抽象類別。新增一個類別MainAdapter繼承抽象類別RecyclerView.Adapter,實作裡面的4個抽象方法。

class MainAdapter(var items: List<String>) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { } override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) { } override fun getItemCount(): Int {

}
override fun getItemViewType(position: Int): Int {

}
}

所以在使用RecyclerView時,你可以自行寫Adpater來繫結Holder裡的資料,只要你寫的這個Adpater是繼承RecyclerView.Adpater就可以了。

--

--