Lambda表达式
Lambda表达式格式要求:
1.一个Lambda表达式总是被一对花括号所包围
2.其参数位于箭头 ->之前,其参数类型可以省略掉
3.执行体位于 -> 之后
4.如果函数的最后一个参数是函数,那么可以将 lambda 表达式作为实参传递进去,并且可以在调用时方法圆括号之外去使用
val multiply: (Int, Int) -> Int = { a, b -> a * b }
这是一个简单的Lambda表达式,Lambda表达式是函数类型,(Int, Int) -> Int 这个是multiply类型,{ a, b -> a * b }这个是multiply的值。
val add: (Int, Int) -> Int = { a, b -> a + b }
{ a, b -> a + b }
这个Lambda表达式中,其参数位于箭头 ->之前,其参数类型可以省略掉,参数是不需要括号括起来的
val subtract = { a: Int, b: Int -> a - b }
可以省略类型,后面参数指定类型
val myAction = { println("Hello World") }
这也是一个Lambda表达式,没有参数,也没有返回结果,返回一个Unit
函数类型可能为null
val myReturnNull: (Int, Int) -> Int? = { _, _ -> null }
接收2个Int,返回一个可null Int,不会用到Lambda表达式的参数,可以用占位符_来代替
ints.filter { it > 0 } // 这个字面值是“(it: Int) -> Boolean”类型的
一个 lambda 表达式只有一个参数,就用it来表示
val functionmybennull: ((Int, Int) -> Int)? = null
整个函数都返回null
高阶函数
参数或者返回值为函数类型的函数,在 Kotlin 中就被称为高阶函数。
参数是函数类型
fun a(funParam: (Int) -> String): String {
return funParam(1)
}
返回值是函数类型
fun c(param: Int): (Int) -> Unit {
...
}
再举一个例子
fun myCalculate(a: Int, b: Int, calculate: (Int, Int) -> Int): Int {
return calculate(a,b)
}
myCalculate(2, 3, { x, y -> x + y })
如果最后一个参数是Lambda表达式,把表达式放到小括号外面。
myCalculate(2, 3) { x, y ->
x + y
}
这个花括号是myCalculate的第三个参数,并不是方法的执行体,这边是函数的使用而不是函数的声明,只有声明的时候才有函数体。
fun String.filter(predicate: (Char) -> Boolean): String {
val ab = StringBuilder()
for (index in 0 until length) {
val element = get(index)
if (predicate(element)) {
ab.append(element)
}
}
return ab.toString()
}
println("abcdef123".filter { it.isLetter() })
val strings = arrayOf("hello", "world", "hello world")
//找到字符串数组如果包含字母h的所有字符串
strings.filter { it.contains("h") }.forEach { println(it) }
//找到长度大于4的字符串
strings.filter { it.length > 5 }.forEach { println(it) }
//找出包含字符d的,将其转换成大写的,可以用映射
strings.filter { it.endsWith("d", true) }.map { it.toUpperCase() }.forEach { println(it) }
不过对于一个声明好的函数,不管是你要把它作为参数传递给函数,还是要把它赋值给变量,都得在函数名的左边加上双冒号才行:
a(::b)
val d = ::b
这……是为什么呢?
如果你上网搜,你会看到这个双冒号的写法叫做函数引用 Function Reference,这是 Kotlin 官方的说法。但是这又表示什么意思?表示它指向上面的函数?那既然都是一个东西,为什么不直接写函数名,而要加两个冒号呢?
因为加了两个冒号,这个函数才变成了一个对象。
什么意思?
Kotlin 里「函数可以作为参数」这件事的本质,是函数在 Kotlin 里可以作为对象存在——因为只有对象才能被作为参数传递啊。赋值也是一样道理,只有对象才能被赋值给变量啊。但 Kotlin 的函数本身的性质又决定了它没办法被当做一个对象。那怎么办呢?Kotlin 的选择是,那就创建一个和函数具有相同功能的对象。怎么创建?使用双冒号。
在 Kotlin 里,一个函数名的左边加上双冒号,它就不表示这个函数本身了,而表示一个对象,或者说一个指向对象的引用,但,这个对象可不是函数本身,而是一个和这个函数具有相同功能的对象。
怎么个相同法呢?你可以怎么用函数,就能怎么用这个加了双冒号的对象:
但我再说一遍,这个双冒号的这个东西,它不是一个函数,而是一个对象,一个函数类型的对象。
对象是不能加个括号来调用的,对吧?但是函数类型的对象可以。为什么?因为这其实是个假的调用,它是 Kotlin 的语法糖,实际上你对一个函数类型的对象加括号、加参数,它真正调用的是这个对象的 invoke() 函数:
d(1) // 实际上会调用 d.invoke(1)
(::b)(1) // 实际上会调用 (::b).invoke(1)
所以你可以对一个函数类型的对象调用 invoke(),但不能对一个函数这么做:
b.invoke(1) // 报错
为什么?因为只有函数类型的对象有这个自带的 invoke() 可以用,而函数,不是函数类型的对象。那它是什么类型的?它什么类型也不是。函数不是对象,它也没有类型,函数就是函数,它和对象是两个维度的东西。
包括双冒号加上函数名的这个写法,它是一个指向对象的引用,但并不是指向函数本身,而是指向一个我们在代码里看不见的对象。这个对象复制了原函数的功能,但它并不是原函数。
从 lambda 表达式中返回一个值
默认情况下,Lambda表达式中的值最后一个表达式的值会隐式作为该Lambda表达式的返回值,我们可以通过限定的return 语法显式返回一个值。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main21)
val strings = arrayOf("hello", "world", "bye")
strings.filter {
val myFilter = it.length > 3
myFilter
}
strings.filter {
val myFilter = it.length > 3
return@filter myFilter
}
}
匿名函数
没有名字的函数,函数体既可以是表达式或者是一个块.
如果参数可以推断出来,参数类型是可以省略掉的
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main21)
fun(x: Int, y: Int) = x + y
fun(x: Int, y: Int): Int {
return x + y
}
}
匿名函数主要用于Lambda中用的,都是没有办法引用的,
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main21)
fun(x: Int, y: Int) = x + y
fun(x: Int, y: Int): Int {
return x + y
}
val strings = arrayOf("hello", "world", "bye")
strings.filter { it.length>3 }.forEach { println(it) }
//换成匿名函数的形式
strings.filter(fun(item): Boolean = item.length > 3).forEach(fun(item) { println(item) })
}
Kotlin 的匿名函数和 Lambda 表达式的本质,它们都是函数类型的对象
闭包
Lambda表达式和匿名函数可以访问作用域外层的变量.Java中是不能修改Lambda表达式外层的变量,要声明成final.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main22)
var sum = ""
val strings = arrayOf("hello", "world", "bye")
strings.filter { it.length > 3 }.forEach {
sum += it
}
println(sum)
}
带接收者的函数字面值
就是我们在声明或者定义一个函数的时候,我们可以指定这个函数被哪一个对象或者哪一个类所使用。通常我们定义一个普通函数,就是被调用,现在我们可以定义一个函数,可以指定这个函数式归属于哪一个类的。
Kotlin提供了这样一种功能:可以通过指定的接收者对象来调用一个函数字面值
在函数字面值内部,你可以调用接收者对象的方法而无需使用任何额外的修饰符
类似于之前讲的扩展函数
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main22)
val subject: Int.(other: Int) -> Int = { other -> this - other }
println(1.subject(3))
}
定义了一个减法,subtrace是一个函数
类型:Int.(other: Int) -> Int 这个变量是函数类型,other本身是一个Int类型的变量,返回类型是整型Int,Int点表示属于Int的一个函数
函数体:= { other -> this - other } this表示Int.(other: Int)中的Int
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main22)
val subject: Int.(other: Int) -> Int = { other -> this - other }
println(1.subject(3))
val sum = fun Int.(other: Int): Int = this + other
println(1.sum(2))
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main22)
val myEquals: String.(Int) -> Boolean = { param -> this.toIntOrNull() == param }
println("456".myEquals(456))
}
String.(Int) -> Boolean 与 (String,Int) ->Boolean 等价
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main22)
fun myTest(op: (String, Int) -> Boolean, a: String, b: Int, c: Boolean) =
println(op(a, b) == c)
myTest(myEquals, "200", 200, true)
}
高阶函数部分内容来自扔物线大神 https://juejin.im/post/6844904116842397710