概览
- Lambda介绍
- Lambda使用
- Lambda表达式的特点
- Lambda语法
- Lambda实践
- it
- 下划线(_)
- 匿名函数
- 带接收者的函数字面值
- 闭包
- 总结
1,Lambda介绍
Kotlin一经开源成熟就已经支持这种语法
Lambda表达式的本质其实是匿名函数,因为在其底层实现中还是通过匿名函数来实现的。Lambda作为函数式编程的基础,其语法也是相当简单的
举例:
tvAddTopic.setOnClickListener {
TopicAddActivity.jumpTo(mContext)
}
2,Lambda使用
2.1,Lambda表达式的特点
- Lambda表达式总是被大括号括着
- 其参数(如果存在)在 -> 之前声明(参数类型可以省略)
- 函数体(如果存在)在 -> 后面。
2.2、Lambda语法
Lambda语法如下
1. 无参数的情况 :
val/var 变量名 = { 操作的代码 }
2. 有参数的情况
val/var 变量名 : (参数的类型,参数类型,...) -> 返回值类型 = {参数1,参数2,... -> 操作参数的代码 }
可等价于
// 此种写法:即表达式的返回值类型会根据操作的代码自推导出来。
val/var 变量名 = { 参数1 : 类型,参数2 : 类型, ... -> 操作参数的代码 }
3. lambda表达式作为函数中的参数的时候,这里举一个例子:
fun test(a : Int, 参数名 : (参数1 : 类型,参数2 : 类型, ... ) -> 表达式返回类型){
...
}
实例
- 无参数的情况
// 源代码
fun test(){ println("无参数") }
// lambda代码
val test = { println("无参数") }
// 调用
test() => 结果为:无参数
- 有参数的情况
// 源代码
fun test(a : Int , b : Int) : Int{
return a + b
}
// lambda
val test : (Int , Int) -> Int = {a , b -> a + b}
// 或者
val test = {a : Int , b : Int -> a + b}
// 调用
fun main() {
println(test(3, 5))
}
test(3,5) => 结果为:8
- lambda表达式作为函数中的参数的时候
// 源代码
fun test(a : Int , b : Int) : Int{
return a + b
}
fun sum(num1 : Int , num2 : Int) : Int{
return num1 + num2
}
//源代码调用
fun main() {
// 结果为:18
println(test(10,sum(3,5)))
}
// lambda
fun test(a : Int , b : (num1 : Int , num2 : Int) -> Int) : Int{
return a + b.invoke(3,5)
}
//调用写法1
fun main() {
// 结果为:18
val value = test(10, { num1: Int, num2: Int -> num1 + num2 })
println(value)
}
//调用写法2
fun main() {
// 结果为:18
val value = test(10) { num1: Int, num2: Int -> num1 + num2 }
println(value)
}
语法的总结
1,lambda
表达式总是被大括号括着。
2,定义完整的Lambda表达式,有其完整的参数类型标注,与表达式返回值。当我们把一些类型标注省略的情况下,就如上面实例中的语法2的另外一种类型。当它推断出的返回值类型不为'Unit'时,它的返回值即为->符号后面代码的最后一个(或只有一个)表达式的类型。
3,高阶函数,当Lambda表达式作为其一个参数时,只为其表达式提供了参数类型与返回类型,所以,我们在调用此高阶函数的时候我们要为该Lambda表达式写出它的具体实现。
4,invoke()
函数:表示为通过函数变量调用自身,因为上面例子中的变量b是一个匿名函数。
Lambda实践
3.1、it
-
it
并不是Kotlin中的一个关键字(保留字)。 -
it
是在当一个高阶函数中Lambda表达式的参数只有一个的时候可以使用it来使用此参数。it
可表示为单个参数的隐式名称,是Kotlin语言约定的。
3.2、下划线()
在使用Lambda表达式的时候,可以用下划线()表示未使用的参数,表示不处理这个参数。
eet_search_friend.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
KeyboardUtils.hideSoftInput(this)
val retrieveInfo = eet_search_friend!!.text.toString().toLowerCase(Locale.ROOT)
mViewModel.userSearch(retrieveInfo)
}
false
}
3.3 匿名函数
- 匿名函数的特点是可以明确指定其返回值类型。
- 它和常规函数的定义几乎相似。他们的区别在于,匿名函数没有函数名。
//常规函数:
fun test(x: Int, y: Int): Int {
return x + y
}
// 匿名函数
fun(x: Int, y: Int): Int {
return x + y
}
常规函数简化 :
fun test(x : Int , y : Int) : Int = x + y
匿名函数简化 :
fun(x : Int , y : Int) : Int = x + y
匿名函数与Lambda表达式的几点区别
- 匿名函数的参数传值,总是在小括号内部传递。而Lambda表达式传值,可以有省略小括号的简写写法。
- 在一个不带标签的return语句中,匿名函数时返回值是返回自身函数的值,而Lambda表达式的返回值是将包含它的函数中返回
3.4、带接收者的函数字面值
在kotlin中,提供了指定的接受者对象调用Lambda表达式的功能。在函数字面值的函数体中,可以调用该接收者对象上的方法而无需任何额外的限定符。它类似于扩展函数,它允你在函数体内访问接收者对象的成员。
- 匿名函数作为接收者类型
匿名函数语法允许你直接指定函数字面值的接收者类型,如果你需要使用带接收者的函数类型声明一个变量,并在之后使用它,这将非常有用。
fun main() {
val iop = fun Int.(other: Int): Int = this + other
println(2.iop(3))
//结果:5
}
- Lambda表达式作为接收者类型
要用Lambda表达式作为接收者类型的前提是接收着类型可以从上下文中推断出来
class HTML {
fun body() {
println("body()")
}
}
/**
* 高阶函数接受一个lambda表达式作为参数.
* @init:参数名称.
* @return 返回HTML类型的对象.
*/
fun html(init: HTML.() -> Unit): HTML {
// 创建接收者对象
val html = HTML()
// 将该接收者对象传给该 lambda
html.init()
return html
}
fun main() {
// 带接收者的 lambda 由此开始
html {
// 调用该接收者对象的一个方法
this.body()
}
}
3.5 闭包
- 所谓闭包,即是函数中包含函数,这里的函数我们可以包含(Lambda表达式,匿名函数,局部函数,对象表达式)。我们熟知,函数式编程是现在和未来良好的一种编程趋势。故而Kotlin也有这一个特性
- 我们熟知,Java是不支持闭包的,Java是一种面向对象的编程语言,在Java中,对象是他的一等公民。函数和变量是二等公民
- Kotlin中支持闭包,函数和变量是它的一等公民,而对象则是它的二等公民了
fun test1(){
fun test2(){ // 正确,因为Kotlin中可以函数嵌套函数
}
}
3.5.1、携带状态
让函数返回一个函数,并携带状态值
fun test(b: Int): () -> Int {
var a = 3
return fun(): Int {
a++
return a + b
}
}
fun main() {
val t = test(3)
println(t())
println(t())
println(t())
}
3.5.2、引用外部变量,并改变外部变量的值
fun main() {
var sum: Int = 0
val arr = arrayOf(1, 3, 5, 7, 9)
arr.filter {
it < 7
}.forEach {
println("it:$it")
sum += it
}
println("sum:$sum")
}
Android中的使用
class TestAdapter(val context: Context, val data: MutableList<String>)
: RecyclerView.Adapter<TestAdapter.TestViewHolder>() {
private var mListener: ((Int, String) -> Unit)? = null
override fun onBindViewHolder(holder: TestViewHolder?, position: Int) {
holder?.itemView?.setOnClickListener {
mListener?.invoke(position, data[position])
}
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): TestViewHolder {
return TestViewHolder(View.inflate(context, layoutId, parent))
}
override fun getItemCount(): Int {
return data.size
}
fun setOnItemClickListener(mListener: (position: Int, item: String) -> Unit) {
this.mListener = mListener
}
inner class TestViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}
// 调用
TestAdapter(this,dataList).setOnItemClickListener {
position, item ->
Toast.makeText(this, "$position \t $item", Toast.LENGTH_SHORT).show()
}