Kotlin类委托(二):实现原理及注意事项
上章留下的问题
有看过我上一篇文章的读者可能会有疑问,为什么对网络数据的请求要使用 lambda
对象 getArticleList
的方式调用,在不同的 viewModel
的 init
方法块中进行设置,为什么不使用在 接口 中声明,在子类中 重写 的方式实现呢?我们改用这样的方式实现看一下效果:
在
ArticleListPagingInterface
中添加方法用于获取数据1
2
3
4
5
6
7
8
9
10/** 分页获取数据相关接口 */
interface ArticleListPagingInterface {
... 省略部分代码 ...
/** 获取文章列表数据 */
fun getArticleList(num: Int): LiveData<NetResult<ArticleListEntity>>
... 省略部分代码 ...
}在
ArticleListPagingInterfaceImpl
中添加对应实现1
2
3
4
5
6
7
8
9
10
11
12
13
14/** 分页获取数据相关接口实现类 */
class ArticleListPagingInterfaceImpl
: ArticleListPagingInterface {
... 省略部分代码 ...
/** 获取文章列表数据 */
override fun getArticleList(num: Int): LiveData<NetResult<ArticleListEntity>> {
// 需要具体业务实现
throw RuntimeException("You must override this method!")
}
... 省略部分代码 ...
}在
BjnewsArticlesViewModel
中重写使用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class 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
}
}运行查看效果,然后你会发现每次获取数据都会抛出异常,重写的
getArticleList
方法并没有生效,这是为什么?
类委托实现原理
查看 Kotlin
对应的 Java
代码
kotlin
语法中很多都是 语法糖,为了能够更好的理解这些语法的规则和使用,我们可以阅读 kotlin
代码对应的 Java
代码,Kotlin 插件 也为我们提供了这样的功能:
通过依次点击 Tools -> Koltin -> Show Kotlin Bytecode -> Decompile,Kotlin 插件 会为我们生成 BjnewsArticleViewModel.decompiled.java
文件,这个就是 BjnewsArticleViewModel.kt
对应的 Java
代码。
类委托的实现
通过对 BjnewsArticleViewModel.decompiled.java
文件的查看,我们发现关键字 by
之后的具体实现对象在 java
中被声明成了 final
修饰的成员变量
并且在构造方法中对其进行了初始化
接口中的方法实现实际上都是调用了对应对象的方法
所以 类委托
的实现实际就是 将接口中对应的方法、变量委托给对应的类实现。
之前的问题
那么我们回到之前的问题,为什么重写了的方法不起作用?
我们可以看到 getArticleList
方法已经是重写了的,但是追踪一下你会发现重写的这个方法并没有任何地方调用,当我们进行网络请求的时候,触发时起源于 ArticleListPagingInterface
中的 pageNumber
的变动,而由于我们使用了 类委托
,在 BjnewsArticleViewModel
中实际进行刷新获取数据的调用的时代理对象 ArticleListPagingInterfaceImpl
中的方法,也就是说加载数据走的是代理对象里面的逻辑,而在 ArticleListPagingInterfaceImpl
中 getArticleList
方法固定抛出异常,因此每次请求数据都会抛出异常。所以对于不同界面可能有的不同数据获取方式,我们选择了声明一个 lambda
对象用于存储获取数据的请求,在不同界面设置不同的获取方式。
因此,使用 类委托
我们确实可以达到 一个类继承多个类 的效果,但是我们需要知道的是这种方式终究不是继承,不能一味的按照继承的思路来实现。
总结
最后,我们得出结论,使用 类委托
我们确实可以达到 一个类继承多个类 的效果,但是我们需要知道的是这种方式终究不是继承,不能一味的按照继承的思路来实现,需要注意以下几点:
- 如果一个 接口 中的方法被 委托类 中的其他方法调用,那么仅仅重写 实现类 中的方法是无效的,必须同时重写 委托类 中调用改方法的方法,或者将对应逻辑修改为对象实现。
- 基于上面一条,不管 委托类 中实现了多少个接口,添加了多少方法,对于 实现类 来说都是不可触及的,
class A: B by BImpl()
中,不管BImpl
有多少逻辑,对于A
来说,关注的永远都只有B
中声明的方法及变量。
那么关于 Kotlin类委托
到这里就说完了,感谢大家的耐心观看,我是 WangJie0822 ,一个普普通通的程序猿,欢迎关注。