一手遮天 Android - kotlin: Lambda 表达式,高阶函数
一手遮天 Android - kotlin: Lambda 表达式,高阶函数
示例如下:
/kotlin/Demo10.kt
/**
* 本例用于演示 Lambda 表达式,高阶函数
*/
package com.webabcd.androiddemo.kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.Toast
import com.webabcd.androiddemo.databinding.ActivityKotlinDemo10Binding
class Demo10 : AppCompatActivity() {
private lateinit var mBinding: ActivityKotlinDemo10Binding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = ActivityKotlinDemo10Binding.inflate(layoutInflater)
setContentView(mBinding.root)
// setOnClickListener 之类的简单写法
sample1()
// Lambda 表达式
sample2()
// 高阶函数
sample3()
// 回调
sample4()
}
fun sample1() {
// 传统方式监听按钮的点击事件
mBinding.button1.setOnClickListener(object: View.OnClickListener{
override fun onClick(v: View?) {
val button = v as Button
// 这里的 this@Demo10 相当于 java 中的 Demo10.this
Toast.makeText(this@Demo10, "${button.text} clicked", Toast.LENGTH_SHORT).show()
}
})
// 在 kotlin 中 setOnClickListener 之类的简单写法
mBinding.button2.setOnClickListener {
// it 就是被点击的按钮
val button = it as Button
// 这里的 this 指向的就是 activity
Toast.makeText(this, "${button.text} clicked", Toast.LENGTH_SHORT).show()
}
}
fun sample2() {
fun1() // fun1
appendMessage(fun2()) // fun2
appendMessage(fun3()) // fun3
appendMessage("${fun4(1, 2)}") // 3
appendMessage("${fun5(1, 2)}") // 3
appendMessage("${fun6(1, 2)}") // 2
}
// 无传入参数,无返回值的 lambda
val fun1 = { appendMessage("fun1") }
// 无传入参数,有返回值的 lambda(自动推断返回值的类型)
val fun2 = { "fun2" }
// 无传入参数,有返回值的 lambda(这里的 () -> String 代表无传入参数,返回值的类型是 String)
val fun3: () -> String = { "fun3" }
// 有传入参数,有返回值的 lambda(自动推断返回值的类型)
// 这里的 -> 的左边是参数声明,右边是表达式
val fun4 = { a: Int, b: Int -> a + b }
// 有传入参数,有返回值的 lambda
// 这里 (Int, Int) -> Int 的意思是有 2 个 Int 类型的传入参数,返回值是 Int 类型的(如果没有返回值,则这里写 -> Unit 即可)
// 这里 { a, b -> a + b } 的意思是 -> 的左边是参数声明,右边是表达式
val fun5: (Int, Int) -> Int = { a, b -> a + b }
// 这里有个约定,如果你后面的表达式用不到某个参数,就用 _ 表示这个参数
val fun6: (Int, Int) -> Int = { _ , b -> b }
// 演示高阶函数(High-Order Function),所谓高阶函数就是将函数用作另一个函数的传入参数或返回值
fun sample3() {
// lambda 表达式作为函数的传入参数的示例
// 下面 2 种写法均可,建议用第 2 种写法(注:这种写法要求函数的最后一个参数是函数,此时就可以把这个最后的参数通过 lambda 表达式的方式放到括号外面)
appendMessage("${funA(1,2, { a, b -> a + b })}") // 3
appendMessage("${funA(1,2) { a, b -> a + b }}") // 3
// 当然,也可以函数作为参数(通过 :: 把指定的函数当做一个参数传递到另一个函数中)
fun myFun(x: Int, y: Int): Int = x + y
appendMessage("${funA(1, 2, ::myFun)}") // 3
// 如果作为参数的函数定义在其他类中的话,则写成类似 className::functionName 即可
appendMessage("${funA(1, 2, Int::plus)}") // 3
// 也可以匿名函数作为参数
appendMessage("${funA(1, 2, fun(x: Int, y: Int): Int { return x + y })}") // 3
// lambda 表达式作为函数的传入参数的示例
// 以下面的例子为例,当 lambda 表达式只有一个参数时,可以用简便写法,自动通过 it 代表这个参数
appendMessage("${funB(3) { x -> x > 5 }}") // 0
appendMessage("${funB(10) { it > 5 }}") // 10
// lambda 表达式作为函数的返回值的示例
appendMessage("${funC(10)(1, 2)}") // 13
// lambda 表达式实现扩展方法的示例
appendMessage("${funD(1, 2)}") // 3
appendMessage("${1.funD(2)}") // 3
appendMessage("${funE(1, 2)}") // 3
appendMessage("${1.funE(2)}") // 3
// lambda 表达式作为扩展方法的参数的示例
appendMessage("${"abc".sumASCII { it.code }}") // 会输出 294(就是 97 + 98 + 99)
// 这里再说明一下 lambda 表达式的返回值的问题
// 有个约定,最后一个表达式的值就是返回值
appendMessage("${"abc".sumASCII { 100 }}") // 300
// 也可以通过 return 显式的返回一个值
appendMessage("${"abc".sumASCII { return@sumASCII 100 }}") // 300
// lambda 表达式实现扩展方法并作为函数的参数,且扩展方法无参数
val a = funF {
// 本例中这个 this 代表的就是字符串 hello:
// 如果你需要调用 this 的方法或属性,那么可以省略 this
appendMessage("$this, ${this.length}, $length") // hello:, 6, 6
this + "webabcd"
}
appendMessage("$a") // hello:webabcd
// 闭包的示例
val b = funG(5)
appendMessage("${b()}, ${b()}, ${b()}") // 5, 10, 15
}
// lambda 表达式作为函数的传入参数的示例
// 这里有个建议的命名规范(operation - 多个传入参数;selector - 一个传入参数且返回值不是 bool 类型;predicate - 一个传入参数且返回值为 bool 类型)
// lambda 表达式中的参数可以有参数名称,也可以省略参数名称
fun funA(a: Int, b: Int, operation: (x: Int, y: Int) -> Int) : Int {
return operation(a, b)
}
// lambda 表达式作为函数的传入参数的示例
// 这里有个建议的命名规范(operation - 多个传入参数;selector - 一个传入参数且返回值不是 bool 类型;predicate - 一个传入参数且返回值为 bool 类型)
fun funB(a: Int, predicate: (Int) -> Boolean) : Int {
return if (predicate(a)) {
a
} else {
0
}
}
// lambda 表达式作为函数的返回值的示例
fun funC(a: Int): (Int, Int) -> Int {
return fun(x, y) : Int {
return a + x + y
}
}
// lambda 表达式实现扩展方法
// 可以类似这么调用 1.funD(2) 或类似这么调用 funD(1, 2)
val funD: Int.(Int) -> Int = { this + it } // 为 Int 类型扩展出 funD(Int) 方法
val funE: Int.(Int) -> Int = Int::plus // 为 Int 类型扩展出 funE(Int) 方法
// lambda 表达式作为扩展方法的参数
// 这里有个建议的命名规范(operation - 多个传入参数;selector - 一个传入参数且返回值不是 bool 类型;predicate - 一个传入参数且返回值为 bool 类型)
fun CharSequence.sumASCII(selector: (Char) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
// lambda 表达式实现扩展方法并作为函数的参数,且扩展方法无参数
// 可以类似这么调用 funF { this + "webabcd" },其中的 this 代表的就是字符串 hello:
fun funF(a: String.() -> String): String { // 在函数内为 String 扩展出了 a() 方法
// 下面这句等同于 return a("hello: ")
return "hello:".a()
}
// 闭包
fun funG(number: Int): () -> Int {
var total = 0
// 这个函数会将其外部的 total 变量捕获过来,也就是说 myFun() 是不会丢失 total 的
val myFun: () -> Int = {
total += number
total
}
// 函数返回的函数就是闭包(闭包引用的闭包外的变量的生命周期会拉长到与闭包一致)
return myFun
}
fun sample4() {
// 经典的回调方式
setOnXxxListener(object: OnXxxListener{
override fun onOk() {
appendMessage("onOk")
}
override fun onError(id: Int, message: String) {
appendMessage("onError: $id, $message")
}
})
mOnXxxListener!!.onOk()
mOnXxxListener!!.onError(-999, "unknown error")
// 通过高阶函数实现单一方法的回调
// setOnYyyListener { result -> appendMessage(result) }
// 如果这个单一方法只有一个参数的话,则上面的可以简写为下面的(自动通过 it 代表这个参数)
setOnYyyListener { appendMessage(it) }
mOnYyyListener!!("webabcd")
}
// 经典的回调方式
private var mOnXxxListener: OnXxxListener? = null
interface OnXxxListener {
fun onOk()
fun onError(id:Int, message:String)
}
private fun setOnXxxListener(listener: OnXxxListener?) {
mOnXxxListener = listener
}
// 通过高阶函数实现单一方法的回调
private var mOnYyyListener: ((String) -> Unit)? = null
private fun setOnYyyListener(listener: ((String) -> Unit)?) {
mOnYyyListener = listener
}
fun appendMessage(message: String) {
mBinding.textView1.append(message);
mBinding.textView1.append("\n");
}
}
/layout/activity_kotlin_demo10.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="传统方式监听按钮的点击事件"/>
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="lambda 方式监听按钮的点击事件"/>
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>