Kotlin 線上讀書會 筆記 (十)介面Interface與抽象類別Abstract Class
介面 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就可以了。