上章留下的问题

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /** 分页获取数据相关接口 */
    interface ArticleListPagingInterface {

    ... 省略部分代码 ...

    /** 获取文章列表数据 */
    fun getArticleList(num: Int): LiveData<NetResult<ArticleListEntity>>

    ... 省略部分代码 ...
    }
  2. 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!")
    }

    ... 省略部分代码 ...
    }
  3. BjnewsArticlesViewModel 中重写使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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 ,一个普普通通的程序猿,欢迎关注。