今天的主题是:函数与Lambda表达式
前言
函数
Kotlin里面的函数其实在之前的学习中已经见过了,通过 fun 关键字来标识
fun double(x: Int): Int {
return 2 * x
}
默认参数
除了一般的使用 x: Int 这种方式定义参数,我们也可以使用默认参数的方式,这种方式可以有效减少重载方法的数量
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {...}
- off是默认值为0的Int类型参数
- len是默认值为b.size的Int类型参数
命名参数
在Java里面,我们调用具有多个参数的方法,是无法直接看到每个参数的意思的,例如:
private void reformat(String str, Boolean normalizeCase, Boolean upperCaseFirstLetter, Boolean, divideByCamelHumps, Char wordSeparator){
...
}
// 调用方法
reformat(str, true, ture, ture, '_')
在Kotlin里面,我们可以在调用的时候给参数加个名字,就像:
fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
……
}
// 调用方法
reformat(str,
normalizeCase = true,
upperCaseFirstLetter = true,
divideByCamelHumps = false,
wordSeparator = '_'
)
这样代码的可读性更高,在多个参数的时候能清晰看到每个参数的作用
返回Unit的函数
前面写的几个方法都有返回值,那么如果要像Java里面一样返回void,在kotlin要怎么写呢?
fun printHello(name: String?): Unit {
if (name != null)
println("Hello ${name}")
else
println("Hi there!")
// `return Unit` 或者 `return` 是可选的
}
通过 Unit 关键字表示返回 Unit类型,他的作用类似于Java里面的void,在Kotlin里面,Unit返回类型是可以省略的。
中缀表示法
标有 infix 关键字的函数也可以使用中缀表示法(忽略该调用的点与圆括号)调用。中缀函数必须满足以下要求:
infix fun Int.shl(x: Int): Int { …… }
// 用中缀表示法调用该函数
1 shl 2
// 等同于这样
1.shl(2)
中缀函数调用的优先级低于算术操作符、类型转换以及 rangeTo
操作符。 以下表达式是等价的:
infix fun Int.shl(x: Int): Int { …… }
// 用中缀表示法调用该函数
1 shl 2
// 等同于这样
1.shl(2)
1 shl 2 + 3
与1 shl (2 + 3)
0 until n * 2
与0 until (n * 2)
xs union ys as Set<*>
与xs union (ys as Set<*>)
另一方面,中缀函数调用的优先级高于布尔操作符 &&
与 ||
、is-
与 in-
检测以及其他一些操作符。这些表达式也是等价的:
a && b xor c
与a && (b xor c)
a xor b in c
与(a xor b) in c
完整的优先级层次结构请参见其语法参考。
Lambda 表达式
关于Lambda表达式这部分,从Java8就开始支持了,在实际开发中也会经常用到,但对我来说还是太抽象了,一直都不太理解,因此这一块我是参照官方文档来写的。如果有好的学习资料,欢迎大家留言~
lambda 表达式与匿名函数是“函数字面值”,即未声明的函数, 但立即做为表达式传递。考虑下面的例子:
max(strings, { a, b -> a.length < b.length })
函数 max
是一个高阶函数,它接受一个函数作为第二个参数。 其第二个参数是一个表达式,它本身是一个函数,即函数字面值,它等价于以下命名函数:
fun compare(a: String, b: String): Boolean = a.length < b.length
从上面的例子看来,其实Lambda就是一种函数的表达方式,这个函数还未声明,但是通过Lambda表达式可以直接调用,只要掌握了语法,就能很方便的写出来了。
Lambda 表达式语法
Lambda 表达式的完整语法形式如下:
val sum = { x: Int, y: Int -> x + y }
lambda 表达式总是括在花括号中, 完整语法形式的参数声明放在花括号内,并有可选的类型标注, 函数体跟在一个 ->
符号之后。如果推断出的该 lambda 的返回类型不是 Unit
,那么该 lambda 主体中的最后一个(或可能是单个)表达式会视为返回值。
如果我们把所有可选标注都留下,看起来如下:
val sum: (Int, Int) -> Int = { x, y -> x + y }
把上面的官方文档说明翻译一下,就是:
1.lambda表达式总是括在花括号中
2.参数在->之前
3.函数在->之后
因此上面的例子翻译一下,就是:
1.{ x, y -> x + y } 为lambda表达式
2.(Int, Int) 为参数
3.Int = {...} 就是一个返回类型为Int的函数
it
:单个参数的隐式名称
一个 lambda 表达式只有一个参数是很常见的。
如果编译器自己可以识别出签名,也可以不用声明唯一的参数并忽略 ->
。 该参数会隐式声明为 it
:
ints.filter { it > 0 } // 这个字面值是“(it: Int) -> Boolean”类型的
几个Tips
Lambda表达式可以传递给一个高阶函数当做参数
fun <T, R> Collection<T>.fold(
initial: R,
combine: (acc: R, nextElement: T) -> R
): R {
var accumulator: R = initial
for (element: T in this) {
accumulator = combine(accumulator, element)
}
return accumulator
}
-
fold这个函数的第二个参数是 combine(R, T),同样是一个函数,他的返回值会作为fold的参数,那么flod就是一个高阶函数,而combine里面可以使用Lambda表达式~
-
如果函数的最后一个参数是一个函数,并且你传递一个 lambda 表达式作为相应的参数,你可以在圆括号之外指定它
例如:
val product = items.fold(1, { acc, e -> acc * e })
=>
val product = items.fold(1) { acc, e -> acc * e }
如果一个函数的参数只有一个,并且参数也是一个函数,那么可以省略圆括号
例如:
run ({ println("...") })
=>
run { println("...") }
总结
由于Kotlin里面Lambda表达式的使用还是很多的,所以多去看看其他的博客.