高阶函数
高阶函数是将函数用作参数或返回值的函数,还可以把函数赋值给一个变量。
所有函数类型都有一个圆括号括起来的参数类型列表以及一个返回类型:(A, B) -> C 表示接受类型分别为 A 与 B 两个参数并返回一个 C 类型值的函数类型。 参数类型列表可以为空,如 () -> A,Unit 返回类型不可省略。
(Int) -> String
函数类型表示法可以选择性地包含函数的参数名:(x: Int, y: Int) -> Point。 这些名称可用于表明参数的含义。
(Button, ClickEvent) -> Unit
如需将函数类型指定为可空,请使用圆括号:((Int, Int) -> Int)?
fun a(funParam: (Int) -> String): String {
return funParam(1)
}
fun b(param: Int): String {
return param.toString()
}
调用
a(::b)
var d = ::b
b(1) // 调用函数
d(1) // 实际上会调用 d.invoke(1)
(::b)(1) // 用对象 :: b 后面加上括号来实现 b() 的等价操作, 实际上会调用 (::b).invoke(1)
b.invoke(1) // 报错
对象是不能加个括号来调用的,但是函数类型的对象可以。为什么?因为这其实是个假的调用,它是 Kotlin 的语法糖,实际上你对一个函数类型的对象加括号、加参数,它真正调用的是这个对象的 invoke() 函数
双冒号
:: 创建一个函数引用或者一个类引用
函数引用
fun isOdd(x: Int) = x % 2 != 0
我们可以很容易地直接调用它(isOdd(5)),但是我们也可以将其作为一个函数类型的值,例如将其传给另一个函数。为此,我们使用 :: 操作符:
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd))
这里 ::isOdd 是函数类型 (Int) -> Boolean 的一个值。
如果我们需要使用类的成员函数或扩展函数,它需要是限定的,例如 String::toCharArray。
val args: Array<String> = arrayOf("1", "2")
args.filter(String::isNotEmpty)
class PdfPrinter {
fun println(any: Any) {
kotlin.io.println(any) //重名了可以用包名调用
}
}
val pdfPrinter = PdfPrinter()
args.forEach(pdfPrinter::println)
类引用
val c = MyClass::class
该引用是 KClass 类型的值
请注意,Kotlin 类引用与 Java 类引用不同。要获得 Java 类引用, 请在 KClass 实例上使用 .java 属性。
平时写的类,其信息都可以在这个KClass来获取
属性引用
data class MediaItem(val title: String, val url: String)
var items= mutableListOf<MediaItem>()
items
.sortedBy { it.title }
.map { it.url }
.forEach { print(it) }
items
.sortedBy(MediaItem::title)
.map(MediaItem::url)
.forEach(::println)
匿名函数
没有名字的函数
要传一个函数类型的参数,或者把一个函数类型的对象赋值给变量,除了用双冒号来拿现成的函数使用,你还可以直接把这个函数挪过来写:
fun b(param: Int): String {
return param.toString()
}
a(fun b(param: Int): String {
return param.toString()
});
val d = fun b(param: Int): String {
return param.toString()
}
//名字没意义,省略
a(fun(param: Int): String {
return param.toString()
});
val d = fun(param: Int): String {
return param.toString()
}
如果你在 Java 里设计一个回调的时候是这么设计的:
public interface OnClickListener {
void onClick(View v);
}
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
使用的时候是这么用的:
view.setOnClickListener(new OnClickListener() {
@Override
void onClick(View v) {
switchToNextPage();
}
});
kotlin写法
fun setOnClickListener(onClick: (View) -> Unit) {
this.onClick = onClick
}
view.setOnClickListener(fun(v: View): Unit) {
switchToNextPage()
})
Lambda写法:
view.setOnClickListener({ v: View ->
switchToNextPage()
})
Lambda 表达式
简化匿名函数,代码更简洁
view.setOnClickListener({ v: View ->
switchToNextPage()
})
//如果 Lambda 是函数的最后一个参数,你可以把 Lambda 写在括号的外面:
view.setOnClickListener() { v: View ->
switchToNextPage()
}
//而如果 Lambda 是函数唯一的参数,你还可以直接把括号去了:
view.setOnClickListener { v: View ->
switchToNextPage()
}
//另外,如果这个 Lambda 是单参数的,它的这个参数也省略掉不写:
//根据上下文推导,根据最后一行代码来推断出返回值类型
view.setOnClickListener {
switchToNextPage()
}
Lambda 表达式的完整语法形式如下:
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
val sum = { x: Int, y: Int -> x + y }
多参数例子:
fold函数:将所提供的操作应用于集合元素并返回累积的结果
val items = listOf(1, 2, 3, 4, 5)
// Lambdas 表达式是花括号括起来的代码块。
items.fold(0, {
// 如果一个 lambda 表达式有