Kotlin函数进阶玩法

公众号「稀有猿诉」 原文链接 More about Kotlin Functions

Kotlin中的函数是一级对象,除了常规的函数式编程以外,还支持一些非常灵活的特殊用法,可以大大增强代码的可读性和简洁性,让代码更加的优雅,在业界顶级的库如Compose中有大量的应用,今天就来学习一些,以扫清学习Compose的障碍。

Extension Functions

与传统的编程语言如C/C++,Java或者Python最大的不同就是,Kotlin对于类的扩展提供了相当灵活的方式。像Java和Python除了标准的继承方式以外,就只能用注解和Decorator。但对于Kotlin还可以用Extensions这一方式。无论是注解还是Decorator,它的使用方式还是比较笨拙的,可以明显的看出来是额外定义的函数,与原Class是没啥关系的。

比如说,对于整数来说,我们通常会有求绝对值,通常可以这样写:

kotlin 复制代码
fun abs(a: Int) = if (a < 0) -a else a

然后,这样使用:

kotlin 复制代码
val aa = abs(a)

但在Kotlin中,有更优雅的方式:

kotlin 复制代码
fun Int.abs() = if (this < 0) -this else this

println((-4).abs())
println(100.abs())

这就是Extension functions,这样定义了后,可以像整数类型本身定义的方法那样直接在其对象上面调用。

如何定义Extension functions

Extension functions是针对Class的,或者一个Type的,指定目标Class名字,和参数就可以了,在函数的内部this就是调用函数时的对象。

kotlin 复制代码
fun <ClassName>.<function name>(params...): return type {
	// this is the function's receiver, which is the object when function invoked.
	// function implementation
}

需要注意,Extension functions必须是针对Class的。

理解Extension functions

Extension functions并没什么高深和神秘的东西,它只是相当于一个static函数,接收目标Class的对象而已,比如说:

kotlin 复制代码
fun Shape.area(): Int = this.length * this.width

fun area(shape: Shape): Int = shape.length * shape.width

其实这两个函数是完全一样的,上面的那个Extension function其实就相当于后面的那个常规函数。只不过在函数的调用上面更加的方便,看起来更像是目标Class提供的方法一样,更优雅一些。

Extension function的作用域

Extension function并不会真的对目标Class做任何修改,它只是相当于你自己定义的一个函数。所以,它的作用域就是你定义的函数的作用域,如果你是在一个文件中定义的,那么它的作用域就是导入了这个文件的地方;如果是在一个类中的,那作用域就是这个类。

另外的问题就是,假如在多个地方定义了相同的Extension function,会发生什么呢,相同的意思就是目标Class一样,函数名字也一样,所做的事情也一样,仍是把它当成普通函数来理解就行,按照虚拟机懒惰加载的原则,应该是第一个被引用到的Extension function生效。

参考资料

Infix Functions

准确的来说是Infix notation,它是一种执行函数的特殊方式,并不是定义了特殊的函数。也就是说某个函数被infix修饰了后,就可以用更为简洁的方式来调用它。常规的函数执行(或者说调用)是用函数名字加上括号,括号里面是参数,比如foo(),bar("here")。而infix方式则可以是 参数1 函数名 参数2这种方式,也即与常规的函数调用完全不一样。看起来像是语言本身的关键字一样。

比如,移位并不是运算符,也不是关键字,而是一个被infix修饰的二元参数函数:

kotlin 复制代码
finfix un Int.shr(x: Int): Int {...}

8.shr(2) // 这样正常调用也完全可以,把整数8右移2位
8 shr 2 // 这是infix式的用法,其实是等同于上面的函数调用

infix必须是Extension function,并且只能有一个参数,算上Extension function的接收对象,其实一共是2个参数。标准库中也定义了大量的infix,如整数位移的shr和shl。以及像一些DSL中的函数,都会定义成infix,以让代码更简洁。

总之,下次再见到 a xyz b这种写法时,不用害怕,并不是有了新的关键字,这里的xyz是infix notation,把它当成函数调用 xyz(a, b)就好了。

参考资料

inline Functions

常规的lambda会有closure(捕获上下文中的对象),在编译后会产生很多对象,这会导致一些性能问题,但这是标准的函数式编程。

但某些情况下,我提供的是一个单纯的行为(lambda),比如像Collections的forEach以及filter,传入的lambda也好,或者其他函数也好,这是一个单纯的行为,你在集合中遍历时执行它就好。这种情况好,我们希望高阶函数在其函数体内直接使用传入的函数参数就可以了,不需要进行常规的对象创建(closure对象以及函数接口对象)。就可以使用inline关键字来修饰这人高阶函数。

参考资料

欢迎搜索并关注 公众号「稀有猿诉」 获取更多的优质文章!

原创不易,「打赏」「点赞」「在看」「收藏」「分享」 总要有一个吧!

相关推荐
Hello.Reader5 分钟前
全面解析 Golang Gin 框架
开发语言·golang·gin
禁默16 分钟前
深入浅出:AWT的基本组件及其应用
java·开发语言·界面编程
Code哈哈笑25 分钟前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
程序猿进阶28 分钟前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
qq_4336184431 分钟前
shell 编程(二)
开发语言·bash·shell
charlie1145141911 小时前
C++ STL CookBook
开发语言·c++·stl·c++20
袁袁袁袁满1 小时前
100天精通Python(爬虫篇)——第113天:‌爬虫基础模块之urllib详细教程大全
开发语言·爬虫·python·网络爬虫·爬虫实战·urllib·urllib模块教程
ELI_He9991 小时前
PHP中替换某个包或某个类
开发语言·php
m0_748236111 小时前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
倔强的石头1061 小时前
【C++指南】类和对象(九):内部类
开发语言·c++