一、函数类型
Kotlin的显式类型声明是:
val sum = { x: Int, y: Int -> x + y }
val action = { println(42) }
// 有两个Int型参数和Int型返回值的函数
val sum: (Int, Int) -> { x, y -> x + y }
// 没有参数和返回值的函数
val action(): -> Unit = { println(42) }
声明函数类型,需要将函数参数类型放在括号中,紧接着是一个箭头和函数的返回类型,如下:
(Int, String) -> Unit
|- 参数类型 -| |- 返回类型 -|
Unit类型用于表示函数不返回任何有用的值。在声明一个普通的函数时,Unit类型的返回值是可以省略的,但是一个函数类型声明总是需要一个**显式的返回类型**,所以在这种场景下**Unit是不能省略**的。
函数类型的返回值是可空类型 & 函数本身可空
- 函数类型的返回值是可空类型
var canReturnNull: (Int?, Int?) -> Int? = { x, y ->
y?.let {
x?.plus(it)
}
}
//测试
println(canReturnNull(1, 2))
println(canReturnNull(null, 2))
//输出结果
3
null
- 函数本身可空
var funOrNull: ((Int, Int) -> Int) ? = null
//测试
println(funOrNull?.let { it(1, 2) })
//输出结果
null
**注意:**如果省略了括号,声明的将会是一个返回值可空的函数类型,而不是一个可空的函数类型的变量.
注意该函数本身可空时调用需要使用**
?类型**
二、调用作为参数的函数
// 定义一个函数类型的参数:operation
fun twoAndThree(operation: (Int, Int) -> Int) {
// 调用函数类型的参数
val result = operation(2, 3)
println("The result is $result")
}
fun twoAndThreeTest() {
twoAndThree{ a, b -> a + b }
twoAndThree{ a, b -> a * b }
}
// 输出结果
The result is 5
The result is 6
调用作为参数的函数和调用普通函数的语法是一样的:把括号放在函数名后,并把参数放在括号内。
函数声明的示意图1:
filter函数以一个判断式作为参数。判断式的类型是一个函数,以字符作为参数并返回boolean类型的值。如果要让传递给判断式的字符出现在最终返回的字符串中,判断式需要返回true,反之返回false。
fun String.filter(predicate: (Char) -> Boolean): String {
val result = StringBuilder()
for(index in 0 until length) {
val element = get(index)
// 调用作为参数传递给“predicate”的函数
if(predicate(element)) result.append(element)
}
return result.toString()
}
fun stringFilterTest() {
// 传递一个lambda作为“predicate”参数
println("abcABC123".filter {
it in 'a' .. 'z'
})
}
// 输出结果
abc
三、在Java中使用函数类
函数类型被声明为普通的接口:一个函数类型的变量时FunctionN接口的一个实现。Kotlin标准库定义了一系列的接口,这些接口对应于不同参数树立的函数:Function0(没有参数的函数)、Function1<P1, R>(一个参数的函数),等等。每个接口定义了一个invoke方法,调用这个方法就会执行函数。
一个函数类型的变量就是实现了对应的**FunctionN接口的实现类的实例**,实现类的**invoke**方法包含了lambda函数体。
/* kotlin 声明 */
fun processTheAnswer(f: (Int) -> Int) {
println(f(42))
}
/* Java */
// 调用processTheAnswer方法
// 在Java代码中使用函数类型(Java8以前)
public static void processTheAnswerTest() {
processTheAnswer(
new Function1<Integer, Integer>() {
@Override
public Integer invoke(Integer number) {
System.out.println(number);
return number + 1;
}
}
);
}
// Java 8
processTheAnswer(number -> number + 1)
在Java中可以很容易地使用Kotlin标准库中以lambda作为参数的扩展函数。
但是要注意:必须要显式地传递一个接受者对象作为第一个参数:
public void printString() {
List<String> strings = new ArrayList<>();
strings.add("42");
// 可以在Java代码中使用Kotlin标准库中的函数
CollectionsKt.forEach(strings, s -> {
System.out.println(s);
// 必须要显式地返回一个Unit类型的值
return Unit.INSTANCE;
});
}
在**Java中,函数或者lambda可以返回Unit。但因为在Kotlin中Unit类型是有一个值的,所以需要显式地返回**它。
注意:一个返回
void的lambda不能作为返回Unit的函数类型的实参! 就像之前的例子中的(String) -> Unit
四、返回函数的函数
enum class Delivery { STANDARD, EXPEDITED }
class Order(val itemCount: Int)
fun getShippingCostCalculator(
// 声明一个返回函数的函数
delivery: Delivery): (Order) -> Double {
if(delivery == Delivery.EXPEDITED) {
// 返回lambda
return { order -> 6 + 2.1 * order.itemCount }
}
// 返回lambda
return { order -> 1.2 * order.itemCount }
}
fun getShippingCostCalculatorTest() {
// 将返回的函数保存在变量中
val calculator =
getShippingCostCalculator(Delivery.EXPEDITED)
// 调用返回的函数
println("Shipping costs ${calculator(Order(3))}")
}
// 输出结果
Shipping costs 12.3
声明一个返回另一个函数的函数,需要指定一个函数类型作为返回类型。
在上述代码中,getShippingCostCalculator返回了一个函数,这个函数以Order作为参数并返回一个Double类型的值。
要返回一个函数,需要写一个return表达式,跟上一个lambda、一个成员引用,或者其他的函数类型的表达式,比如一个(函数类型的)局部变量。