Lambda
可理解为一段代码块,{ }包围,可当做值进行传递,lambda是未声明的函数,以表达式的形式传递
val sum = {num1: Int, num2: Int -> num1 + num2}
-> 前为参数列表,-> 后为要执行的函数体。参数需指定参数类型,若可以推断出类型,可以省略。
val sum2: (x: Int, y: Int) -> Int = { num1, num2 -> num1 + num2 }
把lambda作为函数的参数传递(高阶函数)的简化
定义高阶函数:
private fun testLambdaFun(num1: Int, num2: Int, opt: (x: Int, y: Int) -> Int): Int {
return opt(num1, num2)
}
接收两个Int类型的参数和一个lambda表达式,返回Int类型,lambda表达式的参数是两个Int类型,返回Int类型
调用的几种方式
1.lambda作为变量
testLambdaFun(1, 2, sum)
2.直接传递lambda表达式
testLambdaFun(1, 2, { num1, num2 -> num1 + num2 })
3.lambda表达式是函数的最后一个参数,可移到括号外
testLambdaFun(1, 2) { num1, num2 -> num1 - num2 }
4.除lambda表达式外没有其他参数 () 也可省略
btn.setOnClickListener { v -> ToastUtils.showShort("click") }
btn.setOnClickListener { ToastUtils.showShort("click") }
单个参数可省略,如需使用可用 it 指代
使用lambda可以简化java中匿名内部类的使用,如上面的设置点击监听的方法,像这种只有一个抽象方法的接口称为函数式接口(SAM接口),都可以使用lambda来替代匿名内部类的方式
匿名内部类中只能使用final类型的局部变量,lambda表达式中没有这一限制,可以使用、修改局部变量
常与集合配合使用,kotlin标准库中有很多操作集合的函数如 filter map maxByOrNull all any count find 等
内联函数 inline
编译时期就将函数体替换到调用该函数的地方,节省运行时函数调用的开销
常用的自带内联函数 let with run apply also
let函数
public inline fun <T, R> T.let(block: (T) -> R): R
T类型调用let函数,lambda表达式作为参数,该对象为函数的参数,作用域内it指代,闭包形式返回,返回值由return表达式指定或作用域内最后一行,可用于可空对象的判空后统一处理
testInline?.let {
it.length
it[1]
it.plus("123")
}
with函数
public inline fun <T, R> with(receiver: T, block: T.() -> R): R
接收T类型对象和lambda表达式作为参数,作用域中this指代T类型对象,对象的属性为非空类型时可省略this,直接使用对象属性。闭包形式返回,返回值由return表达式指定或作用域内最后一行
val a = with(testUserDetail.value, {
LogUtils.e("inline0315", "test with = ${this.nickname}")
LogUtils.e("inline0315", "test with = $id")
age
})
或者
val b = with(testUserDetail.value) {
LogUtils.e("inline0315", "test with = ${this.nickname}")
LogUtils.e("inline0315", "test with = $id")
return@with age
}
Run函数
public inline fun <T, R> T.run(block: T.() -> R): R
Let函数与with函数的结合
val a = testUserDetail.value.run {
LogUtils.e("inline0315", "test run = ${this.nickname}")
LogUtils.e("inline0315", "test run = $id")
}
apply函数
public inline fun <T> T.apply(block: T.() -> Unit): T
结构与run函数类似,返回值是调用的对象本身
val a = testUserDetail.value.apply {
this.nickname = "test"
age = 20
}
LogUtils.e("inline0315", "test apply = $a")
应用场景类似于常用的链式调用
also函数
public inline fun <T> T.also(block: (T) -> Unit): T
结构与let函数类似,返回值是调用的对象本身
val a = testUserDetail.value.also {
it.nickname = "test also"
it.age = 21
}
与 reified 关键字使用,实化类型参数函数
inline fun <reified T: BaseViewModel>getViewModel(context: AppCompatActivity): T {
return ViewModelProvider(context).get(T::class.java)
}
reified只能与inline使用,被reified修饰的泛型,可以作为实化类型使用,如这里的T::class.java,若没有使用reified,T不能直接被使用
扩展函数、扩展属性
一般用于给已有三方、系统等无法修改的库新增功能以满足项目需求,扩展不会修改原有代码,不会对原有功能产生影响
fun UserDetailBean.testFun() {
LogUtils.e("extension0315", "test fun nickname = ${this.nickname}")
}
UserDetailBean 为被扩展类型(接收者类型),方法体中this为这个类的对象,即调用者,可省略,但不能访问私有属性和方法,子类可调用父类扩展方法但不能重写
var UserDetailBean.testAttribute: String?
get() {
return this.nickname
}
set(value) {
this.nickname = value
}
必须提供get和set方法
var UserDetailBean.testAttribute: String?
get() {
return this.nickname
}
set(value) {
this.nickname = value
}
fun UserDetailBean.testFun() {
LogUtils.e("extension0315", "test fun nickname = ${this.nickname}")
}
testUserDetail.value.let {
LogUtils.e("extension0315", "test att nickname before = ${it.nickname}")
it.testAttribute = "test attribute"
LogUtils.e("extension0315", "test att nickname after = ${it.nickname}")
it.testFun()
}
extension0315: test att nickname before = null
extension0315: test att nickname after = test attribute
extension0315: test fun nickname = test attribute
高阶函数
定义:函数的参数或者返回值是函数(lambda表达式)
之前提到的操作集合的常用函数、内联函数等多是高阶函数。
注:return从最近的fun关键字返回
如函数A中调用了另一函数B,B的lambda中使用了return 且B是内联函数, 会从A函数返回,如需从B中返回需使用标签 @,若B是非内联函数不可使用return
private fun testReturn() {
fragments.forEach {
LogUtils.e("testReturn0317", "fragment simpleClassName = ${it.javaClass.simpleName}")
if (it is MainFg) {
return
}
}
LogUtils.e("testReturn0317", "after return forEach")
}
testReturn0317: fragment simpleClassName = MainFg
testReturn0317: after return fun
此时return会直接从testReturn函数直接返回,若需从forEach方法中返回需使用标签 xxx@声明 return@
private fun testReturn() {
fragments.forEach label@ {
LogUtils.e("testReturn0317", "fragment simpleClassName = ${it.javaClass.simpleName}")
if (it is MainFg) {
return@label
}
}
LogUtils.e("testReturn0317", "after return forEach")
}
testReturn0317: fragment simpleClassName = MainFg
testReturn0317: after return forEach
testReturn0317: after return fun
或者不声明label 直接return@forEach也可
如果使用了匿名函数,return就会从这个匿名函数返回
private fun testReturn() {
fragments.forEach(fun(fragment) {
LogUtils.e("testReturn0317", "fragment simpleClassName = ${fragment.javaClass.simpleName}")
if (fragment is MainFg) {
return
}
})
LogUtils.e("testReturn0317", "after return forEach")
}
testReturn0317: fragment simpleClassName = MainFg
testReturn0317: after return forEach
testReturn0317: after return fun
如需从外层函数返回可写作return@testReturn
顶层函数
顶层函数可替代Java中使用的静态方法。Java中使用 类名.方法名 的形式来调用静态方法,Kotlin中方法可以不属于任何一个类,可以直接定义在一个kotlin文件中,使用时直接调用方法名即可,除函数外还可以定义顶层属性。Java中也可调用kotlin顶层函数,通过 文件名.方法名 的形式调用。
中缀调用
val testMap = mapOf(Pair(1, "a"), Pair(2, "b"), Pair(3, "c"))
val testMap1 = mapOf(1.to("A"), 2.to("B"), 3.to("C"))
val testMap2 = mapOf(1 to "AA", 2 to "BB", 3 to "CC")
第一种方式是常规创建Pair对象,第二种方式是调用 to 函数,第三种也是函数的调用,称为 中缀调用
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
能被中缀调用的函数在声明时需用infix修饰
解构声明
val (key, value) = 1 to "AAA"
类似的作用可用于map的循环中
for ((key, value) in testMap) {
LogUtils.e("testOther0317", "key = $key, value = $value")
}
testOther0317: key = 1, value = a
testOther0317: key = 2, value = b
testOther0317: key = 3, value = c