博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
# Kotlin 旅途之对象与扩展
阅读量:6035 次
发布时间:2019-06-20

本文共 3271 字,大约阅读时间需要 10 分钟。

Kotlin 旅途之对象与扩展

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

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

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

对象是什么?

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

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

依次来看以下

对象声明

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

左边是实现,右边是调用,这不就是一个单例吗?看来真的名不虚传,果然很简洁,那么为什么用
object 声明的类就自动会实现了单例的功能呢,看一看背后的原理就清楚了:
这不就是在
Java 中我们最常见的一种实现嘛,原来如此,这编译器看来没少在背后替我们操心呢。

对象表达式

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

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

伴生对象

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

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

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

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

扩展

关于扩展,一个简洁明确的概括还是要有的:

  • 扩展是一个简单但是有效的语法糖,能够扩展一个类的新功能而无需继承该类或使用装饰者等模式。 Kotlin 支持扩展函数和扩展属性。快来感受一下它的“威力”~

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

fun 接收者类型.扩展方法() { }复制代码

上图说话

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

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

view.setWidth( xxxUtils.dp2px(50) )复制代码
view.setWidth( 50.dp2px() )复制代码

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

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

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

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

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

B().call(A()) // A.foo in BB1().call(A()) // A.foo in B1B().call(A1()) // A.foo in B复制代码

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

对于

fun Int.isEven() { return it % 2 == 0 }复制代码

其背后实现为:

委托

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

类委托

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

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

属性委托

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

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

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

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

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

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

预告

下一篇文章主要是函数与lambda:

  • 函数
  • lambda 与高阶函数

敬请关注


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

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

声明

本文章首次发布于

转载于:https://juejin.im/post/5b5dd055f265da0fa21a9354

你可能感兴趣的文章
nginx添加系统服务
查看>>
jQuery在IE7和8下setInterval失效的问题
查看>>
谁再问我--分流好 还是 汇聚好。我拍死他!
查看>>
linux命令总结之---cat命令
查看>>
博客迁移
查看>>
我的友情链接
查看>>
cocos2d-x 向android移植问题汇总
查看>>
数据库名实例名服务名
查看>>
centos nginx 启动脚本
查看>>
我的友情链接
查看>>
准确修改虚拟机名称方法
查看>>
为什么你设定的目标最后实现往往都会打折扣?
查看>>
NGINX与APACHE工作模式的区别
查看>>
Linux安装Xcache缓存器
查看>>
谷歌:全球10大爬升最快搜索关键字排行榜 Google Zeitgeist 2011
查看>>
【一天一个shell命令】好管家--查看当前登录用户-w
查看>>
查看所有用户和用户组
查看>>
关于抵御Petya勒索病毒的最新办法
查看>>
Java操作Excel之POI简单例子
查看>>
Hessian入门
查看>>