相關(guān)文章
Kotlin 類委托(一):如何把一個列表頁優(yōu)化到十幾行代碼
上章留下的問題
? 有看過我上一篇文章的讀者可能會有疑問,為什么對網(wǎng)絡(luò)數(shù)據(jù)的請求要使用 lambda 對象 getArticleList 的方式調(diào)用,在不同的 viewModel 的 init 方法塊中進行設(shè)置,為什么不使用在 接口 中聲明,在子類中 重寫 的方式實現(xiàn)呢?我們改用這樣的方式實現(xiàn)看一下效果:
-
在
ArticleListPagingInterface中添加方法用于獲取數(shù)據(jù)/** 分頁獲取數(shù)據(jù)相關(guān)接口 */ interface ArticleListPagingInterface { ... 省略部分代碼 ... /** 獲取文章列表數(shù)據(jù) */ fun getArticleList(num: Int): LiveData<NetResult<ArticleListEntity>> ... 省略部分代碼 ... } -
在
ArticleListPagingInterfaceImpl中添加對應(yīng)實現(xiàn)/** 分頁獲取數(shù)據(jù)相關(guān)接口實現(xiàn)類 */ class ArticleListPagingInterfaceImpl : ArticleListPagingInterface { ... 省略部分代碼 ... /** 獲取文章列表數(shù)據(jù) */ override fun getArticleList(num: Int): LiveData<NetResult<ArticleListEntity>> { // 需要具體業(yè)務(wù)實現(xiàn) throw RuntimeException("You must override this method!") } ... 省略部分代碼 ... } -
在
BjnewsArticlesViewModel中重寫使用class BjnewsArticlesViewModel( private val repository: ArticleRepository ) : BaseViewModel(), ArticleCollectionInterface by ArticleCollectionInterfaceImpl(repository), ArticleListPagingInterface by ArticleListPagingInterfaceImpl() { /** 公眾號 id */ var bjnewsId = "" override fun getArticleList(num: Int): LiveData<NetResult<ArticleListEntity>> { val result = MutableLiveData<NetResult<ArticleListEntity>>() viewModelScope.launch { try { result.value = repository.getBjnewsArticles(bjnewsId, num) } catch (throwable: Throwable) { Logger.t("NET").e(throwable, "getArticleList") } } return result } } 運行查看效果,然后你會發(fā)現(xiàn)每次獲取數(shù)據(jù)都會拋出異常,重寫的
getArticleList方法并沒有生效,這是為什么?
類委托實現(xiàn)原理
查看 Kotlin 對應(yīng)的 Java 代碼
? kotlin 語法中很多都是 語法糖,為了能夠更好的理解這些語法的規(guī)則和使用,我們可以閱讀 kotlin 代碼對應(yīng)的 Java 代碼,Kotlin 插件 也為我們提供了這樣的功能:

? 通過依次點擊 Tools -> Koltin -> Show Kotlin Bytecode -> Decompile,Kotlin 插件 會為我們生成 BjnewsArticleViewModel.decompiled.java 文件,這個就是 BjnewsArticleViewModel.kt 對應(yīng)的 Java 代碼。
類委托的實現(xiàn)
? 通過對 BjnewsArticleViewModel.decompiled.java 文件的查看,我們發(fā)現(xiàn)關(guān)鍵字 by 之后的具體實現(xiàn)對象在 java 中被聲明成了 final 修飾的成員變量

? 并且在構(gòu)造方法中對其進行了初始化
?
? 接口中的方法實現(xiàn)實際上都是調(diào)用了對應(yīng)對象的方法
?
? 所以 類委托 的實現(xiàn)實際就是 將接口中對應(yīng)的方法、變量委托給對應(yīng)的類實現(xiàn)。
之前的問題
? 那么我們回到之前的問題,為什么重寫了的方法不起作用?
?
? 我們可以看到 getArticleList 方法已經(jīng)是重寫了的,但是追蹤一下你會發(fā)現(xiàn)重寫的這個方法并沒有任何地方調(diào)用,當(dāng)我們進行網(wǎng)絡(luò)請求的時候,觸發(fā)時起源于 ArticleListPagingInterface 中的 pageNumber 的變動,而由于我們使用了 類委托,在 BjnewsArticleViewModel 中實際進行刷新獲取數(shù)據(jù)的調(diào)用的時代理對象 ArticleListPagingInterfaceImpl 中的方法,也就是說加載數(shù)據(jù)走的是代理對象里面的邏輯,而在 ArticleListPagingInterfaceImpl 中 getArticleList 方法固定拋出異常,因此每次請求數(shù)據(jù)都會拋出異常。所以對于不同界面可能有的不同數(shù)據(jù)獲取方式,我們選擇了聲明一個 lambda 對象用于存儲獲取數(shù)據(jù)的請求,在不同界面設(shè)置不同的獲取方式。
因此,使用 類委托 我們確實可以達到 一個類繼承多個類 的效果,但是我們需要知道的是這種方式終究不是繼承,不能一味的按照繼承的思路來實現(xiàn)。
總結(jié)
? 最后,我們得出結(jié)論,使用 類委托 我們確實可以達到 一個類繼承多個類 的效果,但是我們需要知道的是這種方式終究不是繼承,不能一味的按照繼承的思路來實現(xiàn),需要注意以下幾點:
- 如果一個 接口 中的方法被 委托類 中的其他方法調(diào)用,那么僅僅重寫 實現(xiàn)類 中的方法是無效的,必須同時重寫 委托類 中調(diào)用改方法的方法,或者將對應(yīng)邏輯修改為對象實現(xiàn)。
- 基于上面一條,不管 委托類 中實現(xiàn)了多少個接口,添加了多少方法,對于 實現(xiàn)類 來說都是不可觸及的,
class A: B by BImpl()中,不管BImpl有多少邏輯,對于A來說,關(guān)注的永遠都只有B中聲明的方法及變量。
? 那么關(guān)于 Kotlin類委托 到這里就說完了,感謝大家的耐心觀看,我是 WangJie0822 ,一個普普通通的程序猿,歡迎關(guān)注。
作者: WangJie0822
鏈接: http://www.wangjie0822.top/posts/438581be
來源: WangJie0822
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。