3.3 函数和Lambda表达式
本节介绍函数的定义、参数、返回类型和Lambda表达式的操作。
3.3.1 函数
Kotlin用fun关键字声明函数,函数的参数必须有显式类型,函数参数可以有默认值,当省略相应的参数时使用默认参数值。覆盖方法总是使用与基类类型方法相同的默认参数值。当覆盖一个带有默认参数值的方法时,必须从签名中省略默认参数值。如果一个默认参数在一个无默认值的参数之前,那么该默认值只能通过使用命名参数调用该函数来使用。如果在默认参数之后的最后一个参数是Lambda表达式,那么它作为命名参数既可以在括号内传入,也可以在括号外传入。函数的参数(通常是最后一个)可以用vararg修饰符修饰。
如果一个函数不返回任何有用的值,那么它的返回类型是Unit。Unit返回类型声明也是可选的。当函数返回单个表达式时,可以省略花括号并且在“=”之后指定代码体。当返回值类型可由编译器推断时,显式声明返回类型是可选的。
标有infix关键字的函数也可以使用中缀表示法(忽略该函数的点与圆括号)调用。中缀函数必须满足以下要求。
它们必须是成员函数或扩展函数。
它们只能有一个参数。
其参数不得接收可变数量的参数且不能有默认值。
中缀函数的优先级低于算术操作符、类型转换及rangeTo操作符。
Kotlin中的函数可以在局部作用域中声明,作为成员函数、顶层函数及扩展函数。局部函数,即一个函数在另一个函数内部,局部函数可以访问外部函数(即闭包)的局部变量。
成员函数是在类或对象内部定义的函数。函数可以有泛型参数,通过在函数名前使用尖括号指定。Kotlin支持一种称为尾递归的函数式编程风格,允许将一些通常用循环写的算法改用递归函数来写,从而避免堆栈溢出的风险。当一个函数用tailrec修饰符修饰并满足所需的形式时,编译器会优化该递归代码,留下一个快速而高效的基于循环的版本。
内联函数可以提高运行时效率,用inline修饰。inline修饰符影响函数本身和传给它的Lambda表达式,所有这些都将内联到调用处。
3.3.2 Lambda表达式
Kotlin中的函数是一等公民,这意味着它们可以存储在变量与数据结构中、作为参数传递给其他高阶函数及从其他高阶函数返回,可以像操作任何其他非函数值一样操作函数。
为促成这一点,作为静态类型编程语言的Kotlin使用一系列函数类型表示函数并提供一组特定的语言结构,例如,Lambda表达式。
高阶函数是将函数用作参数或返回值的函数。fold函数接收一个初始累积值与一个接合函数,并通过将当前累积值与每个集合元素连续接合起来代入累积值来构建返回值。为了调用fold,需要传给它一个函数类型的实例作为参数,而在高阶函数调用处,广泛使用Lambda表达式。
Kotlin使用类似(Int) -> String的一系列函数类型来处理函数的声明。这些类型具有与函数签名相对应的特殊表示法,即它们的参数和返回值。
有几种方法可以获得函数类型的实例。
使用函数字面值的代码块,采用以下形式之一。
Lambda表达式:{ a, b -> a + b }。
匿名函数:fun(s: String): Int { return s.toIntOrNull() ?: 0 }。
使用已有声明的可调用引用。
顶层、局部、成员、扩展函数:::isOdd、String::toInt。
顶层、成员、扩展属性:List<Int>::size。
构造函数:::Regex。
函数类型的值可以通过其invoke()操作符调用:f.invoke(x)或者直接用f(x)。
一个Lambda表达式只有一个参数是很常见的。如果编译器自己可以识别出签名,也可以不用声明唯一的参数并忽略->。该参数会被隐式声明为it:
如果Lambda表达式的参数未使用,那么可以用下画线取代其名称: