Kotlin 朱涛-7 高阶函数 Lambda 闭包 SAM
目录
07 | 高阶函数:函数是一等公民
- StandardKt:with、let、also、takeIf、repeat、apply 源码
- CollectionsKt:map、flatMap、fold、groupBy 源码
Lambda 表达式
Lambda 简介
- Lambda 俗称
匿名函数,也可称为闭包,它是 Java 8 最重要的新特性。 - Lambda 可以理解为是
函数的简写,符合一定条件的函数,就可以使用 Lambda 来简写。 - Lambda 允许把函数作为
参数传递进方法中。
闭包(Closure)就是能够读取其他函数内部变量的函数,所以闭包可以理解成定义在一个函数内部的函数。
SAM 转换
SAM:Single Abstract Method,代表只有一个抽象方法的接口
public void setOnClickListener(OnClickListener l) // 接口符合 SAM 转换条件
fun mOnClick(v: View): Unit = println("onClick") // 一个普通的函数
view.setOnClickListener(::mOnClick) // 引用 mOnClick 函数
view.setOnClickListener { println("onClick") } // 进行 SAM 转换
Lambda 使用案例
下面的代码把一个 Lambda 表达式赋值给了一个变量:
fun hello(string: String) = println("hello $string") // 一个普通的方法
val say: (String) -> Unit = { s: String -> println("say $s") } // 一个闭包(Lambda)
变量 say 的类型是(String) -> Unit
fun main() {
hello("bqt") // hello bqt -- 普通方法的调用
say("bqt") // say bqt ---- 简接调用 invoke 方法
say.invoke("bqt") // say bqt ---- 直接调用 invoke 方法
}
本质是匿名内部类
反编译后 say 的等价 Java 代码为:
public final class MainKt {
private static final Function1 say; // 接口 Function1 的一个实例
static { say = (Function1)null.INSTANCE; } // 接口 Function1 的一个【匿名内部类】
public static final void main() { say.invoke("bqt"); } // 实际上调用的是接口的【invoke】方法
}
其中的 Function1 是 Kotlin 中定义的一个接口,其声明的 invoke 方法只有一个参数:
public interface Function1<in P1, out R> : kotlin.Function<R> {
public abstract operator fun invoke(p1: P1): R
}
Kotlin 中所有定义的 Function 有:
Function0、Function1、Function2...Function22、FunctionN
所以,Lambda 表达式的底层实现,其实是匿名内部类。
函数是一等公民
在 Kotlin 中,函数是一等公民:
- 函数也同样有
类型-- 和类、变量一样也有类型 - 函数也可以被
引用-- 函数也可以被赋值、被作为函数参数、被作为函数返回值 - 函数也可以被
嵌套-- 闭包、Lambda - 函数可
脱离类存在 -- 顶层函数
函数也有类型
类、变量有类型,一等公民函数当然也有。
函数类型:Function Type,是对函数的参数类型和返回值类型的抽象
// 函数类型 (Int, Int) -> Float
// ↑ ↑ ↑
fun add(a: Int, b: Int): Float = (a+b).toFloat()
上面的代码中,(Int, Int) -> Float 就是 add 函数的类型,它代表了参数类型是两个 Int、返回值类型为 Float 的函数类型。
函数也能引用
变量有引用、赋值的概念,一等公民函数当然也有。
fun add(a: Int, b: Int): Int = a + b
val function: (Int, Int) -> Int = ::add // 将函数赋值给变量,【::add】代表函数【add】的引用
val runnable = Runnable { print("x") }
val block: () -> Unit = runnable::run // 【runnable::run】代表对象【runnable】的【run】函数的引用
高阶函数 High-order
高阶函数:High-order functions,是指使用函数作为参数或返回值的函数
在特定业务场景下,可以用高阶函数来实现自己的 DSL (Domain Specific Language)
fun main() {
val runnable = Runnable { println("run") } // 一个普通的对象,实现了 Runnable 接口
val block: () -> Unit = runnable::run // 一个普通的变量,值是某个对象的某个方法
runnable.run() // 执行对象的某个方法
block() // 执行所引用的那个方法
block.invoke() // 同上
fun function1(b: () -> Unit): Unit = b() // 高阶函数,一个使用函数作为参数的方法
fun function2(b: () -> Unit): Unit = b.invoke() // 同上
function1(block) // 执行这个高阶函数,传入的参数是一个函数
function2(block) // 同上
fun function3(): () -> Unit = runnable::run // 高阶函数,一个使用函数作为返回值的方法
fun function4(): () -> Unit = block // 同上
function3()() // 执行这个高阶函数,然后执行返回的那个函数
function4().invoke() // 同上
}
高阶函数的本质是匿名内部类
高阶函数是 Lambda 的一种应用场景,Lambda 的本质是匿名内部类,所以高阶函数的本质也是匿名内部类。
Lambda 表达式的几种写法
① object 匿名内部类
这是原始代码,它的本质是用 object 关键字定义了一个匿名内部类:
image.setOnClickListener(object: View.OnClickListener { // 匿名内部类
override fun onClick(v: View) = gotoPreview(v)
})
② Lambda 表达式
下面是 Lambda 表达式的原始代码:
image.setOnClickListener(View.OnClickListener { v: View -> // Lambda 表达式
gotoPreview(v)
})
③ 省略 SAM 构造器
上面的 View.OnClickListener 被称为 SAM Constructor,Kotlin 的 Lambda 表达式中可以省略:
image.setOnClickListener({ v: View -> // 省略 Lambda 表达式中的 SAM Constructor
gotoPreview(v)
})
④ 支持自动类型推导
Kotlin 支持类型推导,所以类型 View 可以省略:
image.setOnClickListener({ v -> // 省略类型 View
gotoPreview(v)
})
⑤ 省略 Lambda 的参数 ★
当 Kotlin Lambda 表达式只有一个参数的时候,声明中可以省略这个参数,在使用时可以用 it 替代那个参数:
image.setOnClickListener({ // 声明中可以省略这个参数
gotoPreview(it) // 在使用时可以用 it 替代那个参数
})
⑥ 移动 Lambda 位置 ★
当 Kotlin Lambda 作为函数的 最后一个参数 时,Lambda 可以被挪到外面:
image.setOnClickListener() { gotoPreview(it) } // 移动 Lambda 位置
⑦ 省略 Lambda 括号 ★
当 Kotlin 只有一个 Lambda 作为函数参数时,() 可以被省略:
image.setOnClickListener { gotoPreview(it) } // 省略 Lambda 小括号
小结

2016-12-05
本文来自博客园,作者:白乾涛,转载请注明原文链接:https://www.cnblogs.com/baiqiantao/p/6133527.html

浙公网安备 33010602011771号