转载

Kotlin旅途之对象与扩展

每篇文章一句名言(我自己的哈哈)

一门好的编程语言可以让你在使用时最大限度减少对你思维的打断重组,顺行思维方式的语言才会人见人爱。

闲言少叙,在上一篇文章 《Kotlin旅途之类与接口》 中,一起回顾了熟悉的领域(接口、类),也一起探索了未知领域(数据类、密闭类、属性等),由于隐去了很多繁琐的细节,景色更为迷人。趁着兴致勃勃,这篇文章主要介绍 对象 以及 扩展委托

对象是什么?

对象在 Kotlin 中是一个比较综合的概念。其实也可以简单理解为类的实例,这在面向对象的世界中已经司空见惯。我们主要从以下几个方面看看:

  • 对象声明
  • 对象表达式
  • 伴生对象

依次来看以下

对象声明

接下来我们声明一个对象(object),并用它来实现单例模式:

Kotlin旅途之对象与扩展

左边是实现,右边是调用,这不就是一个单例吗?看来真的名不虚传,果然很简洁,那么为什么用 object 声明的类就自动会实现了单例的功能呢,看一看背后的原理就清楚了:

Kotlin旅途之对象与扩展

这不就是在 Java 中我们最常见的一种实现嘛,原来如此,这编译器看来没少在背后替我们操心呢。

对象表达式

延续我们一贯的立场:一图胜千言,有码好说话 ^_^,先上一张图

Kotlin旅途之对象与扩展

这就是一个对象表达式了:用 object 声明一个类并创建它的实例。以上代码就声明并且创建了一个实现 OnClickListener 的对象实例,是不是似曾相识的感觉,嗯这就相当于 Java 中的匿名内部类嘛。话是这么说,但是他们又有不同之处, Kotlin 对其做了功能增强,一是对象表达式可以访问闭包中的变量,但不仅限于 final 变量,原理就像我们在 Java 中显示做的一样,创建了一个 final 类来持有你访问的变量,这省去了我们自己实现的麻烦;二是在与 Java 互交互中所体现的 SAM 转换 ,你应该注意到上方图片的一根警示线,提醒我们有更地道的实现:如果对象是单个抽象方法的 Java 接口,可以用 lambda 表达式代替。

Kotlin旅途之对象与扩展

这样就可以愉快的调用外部的大多数简单接口了

伴生对象

这个主要是 Kotlin 中并不支持 static 关键字,类似于 Java 中静态调用我们所做的那样,我们可以举个例子:

Kotlin旅途之对象与扩展

定义一个类 A ,在 A 中定义它的伴生对象 companion object ,接下来我们就可以直接在 Kotlin 中调用。看起来很像 Java 中的静态调用,然而并没有那么简单。“伴生”二字指明了伴生对象的作用域等于它所属的类(这个例子中是 A )。事实也是如此,在运行时它们仍然是类的实例成员(在 Java 中是静态实例引用)。

Java 中调用:

A.Companion/**名字可自定义,比如 INSTANCE**/.getValue()

看到这基本上你已经清楚了对象是何物了,接下来看看何为 扩展 ,也许这点最初接触这门语言是最吸引人的地方,反正我就是这么被一下子吸引过来的~

扩展

一个简洁明确的概括还是要有的:

  • 扩展是一个简单但是有效的语法糖,能够扩展一个类的新功能而无需继承该类或使用装饰者等模式。

    Kotlin 支持扩展函数和扩展属性。来感受一下它的“威力”~

  • 声明一个扩展可以这样:

fun 接收者类型.扩展方法() { }

上图说话

Kotlin旅途之对象与扩展

上边翻译一下就是,我们给 Int 定义了一个扩展方法 dp2px ,这个方法 Android 同学的老朋友了,根据设备的屏幕分辨率返回指定数值所对应的实际显示像素。功能我们可以暂时不管,如果我们每次都写一串再转换一下,想想都觉得整个人都不好了,工具类在所难免。可是每次都是这样的形式,这样的工具类要一打有一打,一是时间久了我都忘记哪个在哪个里了,二是调用起来也不方便,我要作为参数把 接收者 传递进去。

扩展就很好的帮我们解决了这个痛点,假如我想设置一个 50dp 的显示宽度,我的思维更接近哪一个呢?

view.setWidth( xxxUtils.dp2px(50) )
view.setWidth( 50.dp2px() )

我想你一定会选择第二种,不只是因为直观来看减少了一个参数传递的过程,更因为我们想设置一个宽度这个功能第一要素是 50dp 的宽度值,其次才是转换要素,而不是先想到需要转换成 px ,然后一头扎进一打工具类的海洋中过滤出需要那一个,另外 IDE 足够友好,能帮助我们提示出接收者身上所有的可用扩展方法(或属性),也不必担心侵入接收者类。所以有那么多好处, Kotlin 官方库就用扩展为 Java 库、类等实现了大量简介有用的实现,有机会快去试一试吧。现在,有心动的感觉了吗?

接下来我们看一点稍微复杂但很有必要的一点点东西:

  • 扩展方法 声明 所在的类的实例: 分发接收者
  • 扩展方法 调用 所在的类的实例: 扩展接收者
  • 区别在于:对于前者是 虚拟解析 ,而对后者是 静态解析

区别我们知道了,运行时是有区别的,那么前边两条在讲什么?

Kotlin旅途之对象与扩展

我画了一张图,蓝色的类和对象是分发接收者,红色的类和对象是扩展接收者, A1、B1 分别继承自 A、B ,那么就有:

B().call(A()) // A.foo in B
B1().call(A()) // A.foo in B1
B().call(A1()) // A.foo in B

一目了然,对于 B 类是运行时解析,对应到多态,而对于 A 在编译期就决定了。这也跟实现原理相关,最后放一个原理的小例子,这个相对就简单多了,一看便知:

对于

fun Int.isEven() { return it % 2 == 0 }

其背后实现为:

Kotlin旅途之对象与扩展

委托

委托也是比较大的一块,一旦熟悉了,就很容易理解并运用了。这体现了更倾向于使用 组合 而不是 继承 的理念。

类委托

零样板代码原生支持,上图

Kotlin旅途之对象与扩展

翻译一下就是,定义一个类 A 继承自 Base ,同时委托( by )给一个同样继承自 Base 的类的实现。相信你一下子就想到了著名的 代理模式装饰者模式 (也叫 门面模式 ),但是我们省略掉了很多模版代码。另外如你所想,A类中提供的想同实现会覆盖委托类的实现。更复杂的应用场景留在后边文章讲吧,现在不要耽误我们的行程,继续前进!

属性委托

有了上边的经验,属性委托我们也猜的差不离了,我们可以把一个属性的实现委托给一个类、一个函数都可以。还记得上篇文章我们说过 属性 有对应的 getter setter 嘛,我们要实现一个合格的“代理人”,首先需要一个 getValue ,其次看情况需要一个 setValue 。基本就是这样了,剩下就是具体的实现了。

现在,我们自定义一个属性委托,为了方便我们直接使用 Kotlin 给我们提供好的接口 ReadOnlyProperty (对应 val ) 和 ReadWriteProperty (对应 var ),这俩接口其实就是我们上边所说的一个属性的两个要素。

Kotlin旅途之对象与扩展

有兴趣的可以按照图上码一码试一试,就会 get 到了,很简单

其实标准库已经为我们提供了一些常用的实现,比如 lazy、observable、map 等。篇幅所限,这儿仅看一下 observable 的实现

Kotlin旅途之对象与扩展

上边展示了一个实现了观察者模式的属性,将属性委托给了 observable ,传递了一个初始值 0,之后当 age 变化时,便会通知到处理函数。这是标准库提供的一个定义好的委托。妈妈再也不担心我加班写回调监听了~其他的实现可以去找源码看一下,一看便知。

时间也不早了,今天暂时先到这咯,更加精彩的在后边,敬请期待

下一篇文章主要是对象及扩展:

  • 函数
  • lambda 与高阶函数

敬请关注

另外原文有任何错误改进之处,欢迎联系我修正改进,任何疑问也可以联系我交流。欢迎订阅点赞哦,不定期更新~

声明:此为原创,转载请联系作者

原文  https://xiaozhuanlan.com/topic/2395467108
正文到此结束
Loading...