相关文章
Kotlin 类委托(一):如何把一个列表页优化到十几行代码
上章留下的问题
       有看过我上一篇文章的读者可能会有疑问,为什么对网络数据的请求要使用 lambda 对象 getArticleList 的方式调用,在不同的 viewModel 的 init 方法块中进行设置,为什么不使用在 接口 中声明,在子类中 重写 的方式实现呢?我们改用这样的方式实现看一下效果:
- 
在 ArticleListPagingInterface中添加方法用于获取数据/** 分页获取数据相关接口 */ interface ArticleListPagingInterface { ... 省略部分代码 ... /** 获取文章列表数据 */ fun getArticleList(num: Int): LiveData<NetResult<ArticleListEntity>> ... 省略部分代码 ... }
- 
在 ArticleListPagingInterfaceImpl中添加对应实现/** 分页获取数据相关接口实现类 */ class ArticleListPagingInterfaceImpl : ArticleListPagingInterface { ... 省略部分代码 ... /** 获取文章列表数据 */ override fun getArticleList(num: Int): LiveData<NetResult<ArticleListEntity>> { // 需要具体业务实现 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 } }
- 运行查看效果,然后你会发现每次获取数据都会抛出异常,重写的 - 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 ,一个普普通通的程序猿,欢迎关注。
作者: WangJie0822
链接: http://www.wangjie0822.top/posts/438581be
来源: WangJie0822
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。