公众号「稀有猿诉」 原文链接 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生效。
参考资料
- Extension Functions in Kotlin
- Kotlin Extension Function: How To Implement And Use It
- 让人爱不释手的Kotlin扩展(Extensions)技术探Extensions秘与应用
- Kotlin 扩展函数及原理
- Kotlin 编程 #3 扩展函数(终于知道为什么 with 用 this,let 用 it)
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关键字来修饰这人高阶函数。
参考资料
欢迎搜索并关注 公众号「稀有猿诉」 获取更多的优质文章!
原创不易,「打赏」 ,「点赞」 ,「在看」 ,「收藏」 ,「分享」 总要有一个吧!