Kotlin学习之旅(D4)-函数与Lambda表达式

Kotlin学习之旅-第四天

今天的主题是:函数与Lambda表达式

前言

Kotlin学习之旅(D1)-学习计划&基本语法

Kotlin学习之旅(D2)-基本语法

Kotlin学习之旅(D3)-类与继承

函数

Kotlin里面的函数其实在之前的学习中已经见过了,通过 fun 关键字来标识

1
2
3
fun double(x: Int): Int {
return 2 * x
}

默认参数

除了一般的使用 x: Int 这种方式定义参数,我们也可以使用默认参数的方式,这种方式可以有效减少重载方法的数量

1
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {...}
  1. off是默认值为0的Int类型参数
  2. len是默认值为b.size的Int类型参数

命名参数

在Java里面,我们调用具有多个参数的方法,是无法直接看到每个参数的意思的,例如:

1
2
3
4
5
6
private void reformat(String str, Boolean normalizeCase, Boolean upperCaseFirstLetter, Boolean, divideByCamelHumps, Char wordSeparator){
...
}

// 调用方法
reformat(str, true, ture, ture, '_')

在Kotlin里面,我们可以在调用的时候给参数加个名字,就像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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要怎么写呢?

1
2
3
4
5
6
7
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 关键字的函数也可以使用中缀表示法(忽略该调用的点与圆括号)调用。中缀函数必须满足以下要求:

1
2
3
4
5
6
7
infix fun Int.shl(x: Int): Int { …… }

// 用中缀表示法调用该函数
1 shl 2

// 等同于这样
1.shl(2)

中缀函数调用的优先级低于算术操作符、类型转换以及 rangeTo 操作符。 以下表达式是等价的:

  • 1 shl 2 + 31 shl (2 + 3)
  • 0 until n * 20 until (n * 2)
  • xs union ys as Set<*>xs union (ys as Set<*>)

另一方面,中缀函数调用的优先级高于布尔操作符 &&||is-in- 检测以及其他一些操作符。这些表达式也是等价的:

  • a && b xor ca && (b xor c)
  • a xor b in c(a xor b) in c

完整的优先级层次结构请参见其语法参考

Lambda 表达式

关于Lambda表达式这部分,从Java8就开始支持了,在实际开发中也会经常用到,但对我来说还是太抽象了,一直都不太理解,因此这一块我是参照官方文档来写的。如果有好的学习资料,欢迎大家留言~

lambda 表达式与匿名函数是“函数字面值”,即未声明的函数, 但立即做为表达式传递。考虑下面的例子:

1
max(strings, { a, b -> a.length < b.length })

函数 max 是一个高阶函数,它接受一个函数作为第二个参数。 其第二个参数是一个表达式,它本身是一个函数,即函数字面值,它等价于以下命名函数:

1
fun compare(a: String, b: String): Boolean = a.length < b.length

从上面的例子看来,其实Lambda就是一种函数的表达方式,这个函数还未声明,但是通过Lambda表达式可以直接调用,只要掌握了语法,就能很方便的写出来了。

Lambda 表达式语法

Lambda 表达式的完整语法形式如下:

1
val sum = { x: Int, y: Int -> x + y }

lambda 表达式总是括在花括号中, 完整语法形式的参数声明放在花括号内,并有可选的类型标注, 函数体跟在一个 -> 符号之后。如果推断出的该 lambda 的返回类型不是 Unit,那么该 lambda 主体中的最后一个(或可能是单个)表达式会视为返回值。

如果我们把所有可选标注都留下,看起来如下:

1
val sum: (Int, Int) -> Int = { x, y -> x + y }

把上面的官方文档说明翻译一下,就是:

1
2
3
1.lambda表达式总是括在花括号中
2.参数在->之前
3.函数在->之后

因此上面的例子翻译一下,就是:

1
2
3
1.{ x, y -> x + y } 为lambda表达式
2.(Int, Int) 为参数
3.Int = {...} 就是一个返回类型为Int的函数

it:单个参数的隐式名称

一个 lambda 表达式只有一个参数是很常见的。

如果编译器自己可以识别出签名,也可以不用声明唯一的参数并忽略 ->。 该参数会隐式声明为 it

1
ints.filter { it > 0 } // 这个字面值是“(it: Int) -> Boolean”类型的

几个Tips

  1. Lambda表达式可以传递给一个高阶函数当做参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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表达式~

  2. 如果函数的最后一个参数是一个函数,并且你传递一个 lambda 表达式作为相应的参数,你可以在圆括号之外指定它

    例如:

    1
    2
    3
    val product = items.fold(1, { acc, e -> acc * e }) 
    =>
    val product = items.fold(1) { acc, e -> acc * e }
  3. 如果一个函数的参数只有一个,并且参数也是一个函数,那么可以省略圆括号

    例如:

    1
    2
    3
    run ({ println("...") })
    =>
    run { println("...") }

总结

由于Kotlin里面Lambda表达式的使用还是很多的,所以建议大家多去看看其他的博客,因为我觉得官方文档对于没接触过Lambda的初学者来说还是太难懂了一点。

Day 4 - Learn Kotlin Trip, Completed.


本文结束啦感谢您的阅读