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关键字来修饰这人高阶函数。

参考资料

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

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

相关推荐
hccee13 分钟前
C# IO文件操作
开发语言·c#
hummhumm18 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
J老熊28 分钟前
JavaFX:简介、使用场景、常见问题及对比其他框架分析
java·开发语言·后端·面试·系统架构·软件工程
zmd-zk42 分钟前
flink学习(2)——wordcount案例
大数据·开发语言·学习·flink
好奇的菜鸟1 小时前
Go语言中的引用类型:指针与传递机制
开发语言·后端·golang
Alive~o.01 小时前
Go语言进阶&依赖管理
开发语言·后端·golang
花海少爷1 小时前
第十章 JavaScript的应用课后习题
开发语言·javascript·ecmascript
手握风云-1 小时前
数据结构(Java版)第二期:包装类和泛型
java·开发语言·数据结构
喵叔哟1 小时前
重构代码中引入外部方法和引入本地扩展的区别
java·开发语言·重构
尘浮生1 小时前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea