Kotlin学习之Lambda表达式和高阶函数

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

猜你喜欢

转载自blog.csdn.net/jingerlovexiaojie/article/details/107708237