您的位置:首页 > 移动开发 > Android开发

解析Kotlin函数和属性扩展

2017-10-13 19:43 363 查看
最近开始学习Kotlin,看好kotlin,有不少语法糖啊,先在android平台站稳脚跟,然后慢慢向其他平台延伸



引言

一直听说kotlin有扩展功能,可对类进行
函数和属性扩展
,想着这功能666啊,今天正好学到这里,特别关注了下其实现,通过
跑代码和编译class文件
,加深了对kotlin扩展功能的理解

这篇文章的重点有:

举几个扩展函数的栗子

编译.kt文件为.class文件,运行

理解kotlin扩展功能的实现原理

方法参数的类型重载

引用官方描述:

Kotlin, similar to C# and Gosu, provides the ability to extend a class with new functionality without having to inherit from the class or use any type of design pattern such as Decorator. This is done via special declarations called extensions. Kotlin supports extension functions and extension properties.

Simply就是Kotlin无需通过继承或装饰器模式,即可为特定类扩展出新的函数和属性,然后就可以直接使用了



1.举一个简单栗子:

fun main(args: Array<String>) {
println("123".extra())
}

fun String.extra() = "长度是" + this.length


然后是官方的栗子:

fun main(args: Array<String>) {
val list = mutableListOf(1, 2, 3)
list.swap(0, 2)
println(list)//输出3、2、1
}

fun <T> MutableList<T>.swap(x: Int, y: Int) {
val tmp = this[x]
this[x] = this[y]
this[y] = tmp
}


2.开始吃官方的栗子

2.1 编译为jar

在桌面上vim一个Extension.kt文件,写入上述代码;

然后执行命令



这一步就是编译Extension.kt为jar,结束后在桌面上能看到Extension.jar

无法执行的请执行以下命令配置环境,然后重试第一步(PS:可能会下载几次都会失败,原因你懂的,如果实在不行,请自行搜索如何copy studio下的文件夹,因为我没试过这种方式..)

$ sudo port install kotlin


2.2 运行jar

对jar执行运行命令



就可以看到运行结果,可以看到对可变数组进行反转功能的扩展函数生效了

看到结果我们不能满足,还应该看看这货究竟是怎么实现的

3.理解扩展函数实现

理解扩展实现需要查看编译后的class文件,有以下两种方式可得到class文件:

直接修改上述生成的Extension.jar为Extension.zip,解压后就可以在顶级文件夹内拿到Extension.class文件

执行命令编译Extension.kt文件为Extension.class,这和javac是一样的步骤,如下图



这样我们就拿到了编译后的class文件

再用jd-jui打开Extension.class文件一探究竟



重点关注swap函数,对比之前的kotlin实现,我们就可以发现,所谓的扩展函数不过是披了层装饰器模式的外衣,剥开了还是最基本的实现,只不过装饰器的外衣kotlin是披在了语言级别,而普通的装饰器模式的实现是代码级别的,酱紫写kotlin的众码们就可以无感的认真撕业务代码了;

当然我们也可以用Java代码实现代码级别的装饰器模式,同样能实现函数和属性扩展,直接copy出来就阔以了。。。



窥一斑可见全豹

这就是扩展函数的实现原理,kotlin提供这个语法糖,我们就可快速扩展函数和属性,而剥离装饰器的外衣这一步就在编译过程中被kotlin代劳了

通过上面的认识,我们也就无需再看扩展属性是如何实现的了,一样样的原理

4.扩展是被静态解析的

官方提了另一个栗子,代码如下:

fun main(args: Array<String>) {
printFoo(D())
}

open class C

class D : C()

fun C.foo() = println("c")

fun D.foo() = println("d")

fun printFoo(c: C) {
println(c.foo())
}


执行上述代码我们得到的结果是: “c”

但是不应该输出 “d”么???

看看通过查看编译后的class文件我们是否能有所获,class内容如下:



我们可以看到

扩展函数在这里被编译为两个重载函数,重载函数的参数类型分别为C和D

在创建D的实例时,将实例转为了C

如果看到这里你对kotlin扩展已经了然于胸,那就无需往下看拉!!!

其实重点在于printFoo方法,其参数是C类型,则调用重载方法时已确定了类型,所以打印出来”c”,且任意调用printFoo方法的函数都会默认把入参转为C类型,就酱紫,==。。

我们写一段Java代码;

public class JavaMain {
public static void main(String[] args) {
printFoo(new D());//入参在printFoo方法内确定为C类型
foo(new D());//入参为D类型
foo((C) new D());//入参为C类型
}

public static final void foo(C $receiver) {
String str = "c";
System.out.println(str);
}

public static final void foo(D $receiver) {
String str = "d";
System.out.println(str);
}

public static final void printFoo(C instance) {
foo(instance);
}

static class C {
}

static class D extends C {
}
}


打印的结果即为

c
d
c


可见这一点是符合Java的特性的;



结语

从本文过程中我们理解了

如何编译kotlin文件和查看class文件

理解kotlin的扩展实现原理

方法参数的类型重载

附注:

Kotlin的学习文档可以 戳这里

官方也支持 kotlin在线编程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息