阅读 215

Kotlin 类委托(二):实现原理及注意事项

相关文章

Kotlin 类委托(一):如何把一个列表页优化到十几行代码

Kotlin 类委托(二):实现原理及注意事项

上章留下的问题

​ 有看过我上一篇文章的读者可能会有疑问,为什么对网络数据的请求要使用 lambda 对象 getArticleList 的方式调用,在不同的 viewModelinit 方法块中进行设置,为什么不使用在 接口 中声明,在子类中 重写 的方式实现呢?我们改用这样的方式实现看一下效果:

  1. ArticleListPagingInterface 中添加方法用于获取数据

    /** 分页获取数据相关接口 */
    interface ArticleListPagingInterface {
    
        ... 省略部分代码 ...
        
        /** 获取文章列表数据 */
        fun getArticleList(num: Int): LiveData<NetResult<ArticleListEntity>>
        
        ... 省略部分代码 ...
    }
    复制代码
  2. ArticleListPagingInterfaceImpl 中添加对应实现

    /** 分页获取数据相关接口实现类 */
    class ArticleListPagingInterfaceImpl
        : ArticleListPagingInterface {
    	        
        ... 省略部分代码 ...
        
        /** 获取文章列表数据 */
        override fun getArticleList(num: Int): LiveData<NetResult<ArticleListEntity>> {
            // 需要具体业务实现
            throw RuntimeException("You must override this method!")
        }
            
        ... 省略部分代码 ...
    }
    复制代码
  3. 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
        }
    }
    复制代码
  4. 运行查看效果,然后你会发现每次获取数据都会抛出异常,重写的 getArticleList 方法并没有生效,这是为什么?

类委托实现原理

查看 Kotlin 对应的 Java 代码

kotlin 语法中很多都是 语法糖,为了能够更好的理解这些语法的规则和使用,我们可以阅读 kotlin 代码对应的 Java 代码,Kotlin 插件 也为我们提供了这样的功能:

显示Java代码

​ 通过依次点击 Tools -> Koltin -> Show Kotlin Bytecode -> DecompileKotlin 插件 会为我们生成 BjnewsArticleViewModel.decompiled.java 文件,这个就是 BjnewsArticleViewModel.kt 对应的 Java 代码。

类委托的实现

​ 通过对 BjnewsArticleViewModel.decompiled.java 文件的查看,我们发现关键字 by 之后的具体实现对象在 java 中被声明成了 final 修饰的成员变量

变量声明

​ 并且在构造方法中对其进行了初始化

变量初始化

​ 接口中的方法实现实际上都是调用了对应对象的方法

方法重写

​ 所以 类委托 的实现实际就是 将接口中对应的方法、变量委托给对应的类实现

之前的问题

​ 那么我们回到之前的问题,为什么重写了的方法不起作用?

重写获取数据方法

​ 我们可以看到 getArticleList 方法已经是重写了的,但是追踪一下你会发现重写的这个方法并没有任何地方调用,当我们进行网络请求的时候,触发时起源于 ArticleListPagingInterface 中的 pageNumber 的变动,而由于我们使用了 类委托,在 BjnewsArticleViewModel 中实际进行刷新获取数据的调用的时代理对象 ArticleListPagingInterfaceImpl 中的方法,也就是说加载数据走的是代理对象里面的逻辑,而在 ArticleListPagingInterfaceImplgetArticleList 方法固定抛出异常,因此每次请求数据都会抛出异常。所以对于不同界面可能有的不同数据获取方式,我们选择了声明一个 lambda 对象用于存储获取数据的请求,在不同界面设置不同的获取方式。

因此,使用 类委托 我们确实可以达到 一个类继承多个类 的效果,但是我们需要知道的是这种方式终究不是继承,不能一味的按照继承的思路来实现。

总结

​ 最后,我们得出结论,使用 类委托 我们确实可以达到 一个类继承多个类 的效果,但是我们需要知道的是这种方式终究不是继承,不能一味的按照继承的思路来实现,需要注意以下几点:

  1. 如果一个 接口 中的方法被 委托类 中的其他方法调用,那么仅仅重写 实现类 中的方法是无效的,必须同时重写 委托类 中调用改方法的方法,或者将对应逻辑修改为对象实现。
  2. 基于上面一条,不管 委托类 中实现了多少个接口,添加了多少方法,对于 实现类 来说都是不可触及的,class A: B by BImpl() 中,不管 BImpl 有多少逻辑,对于 A 来说,关注的永远都只有 B 中声明的方法及变量。

​ 那么关于 Kotlin类委托 到这里就说完了,感谢大家的耐心观看,我是 WangJie0822 ,一个普普通通的程序猿,欢迎关注。

作者: WangJie0822 链接: www.wangjie0822.top/posts/43858… 来源: WangJie0822 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

文章分类
Android
文章标签