转载自: https://blog.csdn.net/zhongyili_sohu/article/details/105240947
1
(1)匿名函数和普通函数没什么区别,只是没有名字而已
(2)如果是单表达式的函数体,可以用赋值运算符代替大括号,而且可以省略返回值类型,编译器可以推断出是否有返回值,如果有类型是什么类型。这个其实和匿名函数没关系,普通函数也通用
(3)如果编译器可以推断出匿名函数的形参类型,可以将形参类型省略
(4)匿名函数是可以作为函数参数的,在实际使用中函数参数的类型是lambda函数类型,可是传个匿名函数是没问题的,这也能说明,lambda的底层实现是匿名函数,比如filter方法
class FirstKotlinClass {
//普通函数
// fun add(x : Int, y : Int) : Int {
// return x + y
// }
//匿名函数,赋值给add变量
// var add = fun (x : Int, y : Int) : Int {
// return x + y
// }
//匿名函数单表达式函数体,简写,可以省略返回值类型
var add = fun (x : Int, y : Int) = x + y
//
var count = 5
// fun generateAnswerString(countThreshold: Int): String {
// return if (count > countThreshold) {
// "I have the answer."
// } else {
// "The answer eludes me."
// }
// }
//普通函数单表达式函数体,简写,可以省略返回值类型
fun generateAnswerString(countThreshold : Int) = if (count > countThreshold) {
"I have the answer."
} else {
"The answer eludes me."
}
var filteredList = listOf(3, 5, 20, 100, -25).filter(
//编译器能推断出匿名函数形参的类型,可以省略
// fun(el):Boolean {
// return Math.abs(el) > 20
// }
//单表达式的匿名函数
fun (el) = Math.abs(el) > 20
)
}
lambda表达式的底层实现式匿名函数,lambda表达式的出现式进一步对匿名函数简化,我是这样理解的
在kotlin语言,对类型这个概念进一步抽象了,类、基本类型都是类型,匿名函数也能抽象出类型(叫函数类型),而且类型在kotlin中是放在变量名+冒号(:)后面的,函数类型就需要和lambda表达式一块使用了。看一个例子
var add1 : (Int, Int) -> Int = {a, b -> a + b} //a和b的类型省略了,是因为等号前面说明了
//进一步简化
var add2 = {a : Int, b : Int -> a + b}
(Int, Int) -> Int
这个叫函数类型,
add1叫函数类型的变量,
{a, b -> a + b}是变量的值。
这里大家不要混淆了,本质上这个还是函数,只是使用lambda语法简化了,要想使用函数还是得调用才行呀!
既然函数可以抽象成一个类型,那么函数类型就可以作为方法的参数传,这样的话,就给编程带来极大的灵活性
//(Int, Int) -> Int 函数类型
fun test(a : Int, b : (Int, Int) -> Int) : Int {
return a + b(2, 3)
}
//{num1, num2 -> num1 + num2} 函数类型的值
var res1 = ins.test(10, {num1, num2 -> num1 + num2})
println(res1)
这些写代码是不是很爽呀,比java没lambda表达式前,传一个类对象,调用类对象方法,简洁多了。
上面的调用,会有个下划线提示 Lambda argument should be moved out of parentheses,这个意思就是说,需要把lambda表达式移到括号外面 。这是因为,如果lambda表达式是函数参数的最后一个参数,它可以写到括号外面
var res1 = ins.test(10) { num1, num2 -> num1 + num2}
我理解Lambda表达式,是由函数类型、变量和值 三者组成的,有时也把函数类型变量的值叫做lambda表达式,不知道自己理解的对不对。
只有单表达式或者语句的函数才能转换成lambda的形式,也不知道这句话对不对。
上面这句话是不对的,发现了有多条语句的lambda表达式
val mTvBtn = findViewById<TextView>(R.id.text)
mTvBtn.run{
text = "kotlin"
textSize = 13f
}
“表达式“是有结果的,也就是说可以作为等号的右值。“语句”就是一行执行代码,没有返回值
Lambda表达式的出现,把函数提升到了和类一样的地位,函数不需要再依附于类才能使用,我觉得这是Lambda表达式最重要的意义。同时Lambda还带来一个好处就是使代码简洁了,看看上面的lambda表达式的使用就知道了。
为了简洁,lambda一个参数的,还可以更简洁,用it代替(注意it不是kotlin的关键字,只是约定),->和左边的参数变量都可以省略了
fun test(num1 : Int, bool : (Int) -> Boolean) : Int{
return if (bool(num1)){ num1 } else 0
}
println(test(10,{it > 5}))
println(test(4,{it > 5}))
虽然lambda表达式的底层式匿名函数,可是还是有不一样的地方。
匿名函数的本质依旧是函数,因此匿名函数的return则用于返回该函数本身。
而Lambda表达式的return用于返回它所在的函数,而不是返回Lambda表达式。
还有一个概念是函数字面值(量),其实就是指一个函数体,或者说是一段代码,本身没有名字,我们可以把它绑定到一个变量上,通过这个变量操作它,lambda表达式和匿名函数都叫函数字面值。
这里说一句题外话,kotlin不管怎么写,本质上脱离不了jvm字节码的,这个是根本,只不过kotlin是让大家写代码更自由更简洁一些。
我们在java中定义一些工具类方法的时候,我们一般把这类方法定义成类方法。在kotlin中就不用这样干了,直接在文件中定义方法就行,不用类包裹了,不过编译器最后还是把它编译成了类方法。
(在Object C中的Block,可以类比于Kotlin的匿名函数,因为整个函数的结构还在,只是没有方法名而已。它没有抽象成lambda表达式)
kotlin中的匿名函数和js中一样,可以灵活使用,不过,js里面的函数是没有返回类型的,在kotlin匿名函数有返回类型的一定要说明,不写默认是Unit,Unit就是java中void的意思。
//匿名函数不需要赋给变量,也能直接调用,和js一样方便
var res = (fun (y : Int, x : Int) : Int {
return y + x
})(1, 2)
println(res)
//lambda表达式直接调用
({x : Int, y : Int -> x + y})(1, 2)
//闭包
var funInner = (fun ():() -> Unit {
var i = 10
return fun () {
println(++i)
}
})()
funInner()
funInner()
2
简化setOnClickListener()
我们用Android中非常典型的例子去解释它是怎么工作的:View.setOnClickListener()
方法。如果我们想用Java的方式去增加点击事件的回调,我首先要编写一个OnClickListener
接口:
public interface OnClickListener {
void onClick(View v);
}
然后我们要编写一个匿名内部类去实现这个接口:
view.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Click", Toast.LENGTH_SHORT).show();
}
})
我们将把上面的代码转换成Kotlin(使用了Anko的toast函数):
view.setOnClickListener(object : OnClickListener {
override fun onClick(v: View) {
toast("Click")
}
}
//object关键字声明匿名内部类的语法形式:
//object [ : 接口1,接口2,类型1, 类型2]{} //中括号中的可省略
很幸运的是,Kotlin允许Java库的一些优化,Interface中包含单个函数可以被替代为一个函数。如果我们这么去定义了,它会正常执行:
fun setOnClickListener(listener: (View) -> Unit)
一个lambda表达式通过参数的形式被定义在箭头的左边(被圆括号包围),然后在箭头的右边返回结果值。在这个例子中,我们接收一个View,然后返回一个Unit(没有东西)。所以根据这种思想,我们可以把前面的代码简化成这样:
view.setOnClickListener({ view -> toast("Click")})
//自我猜测若要用到view则: view.setOnClickListener(view -> Unit ){toast("Click")}
这是非常棒的简化!当我们定义了一个方法,我们必须使用大括号包围,然后在箭头的左边指定参数,在箭头的右边返回函数执行的结果。如果左边的参数没有使用到,我们甚至可以省略左边的参数:
view.setOnClickListener({ toast("Click") })
如果这个函数的最后一个参数是一个函数,我们可以把这个函数移动到圆括号外面:
view.setOnClickListener() { toast("Click") }
并且,最后,如果这个函数只有一个参数,我们可以省略这个圆括号:
view.setOnClickListener { toast("Click") }
比原始的Java代码简短了5倍多,并且更加容易理解它所做的事情。非常让人影响深刻。
3(我的理解)
注意lambda分为传参(实质就是匿名类,而匿名类的实质就是对象,古lambda表达式可以理解为除去 ::aFunction 以外的函数式对象的写法)和形参,
当形参里面确认了返回值和参数类型,传参里面是可以自动推断的
由于lambda是个代码块,故根据最后一行来判断lambda表达式返回类型
个人理解总结如下:
形参(函数式类)简化过程:
Interface Listener{
fun listener(view: View)
}
- 方法形参:
fun setOnclickListener(listener: Listener)
// to lambda
fun setOnClickListener(listener: (View) -> Unit)
- 类形参
class MyOnClickListener(val listener: Listener)
//面向函数式编程,无疑是面向函数或方法编程
//本来原来的类的形参是一个类,但是传入类的目的实际是使用类下的方法
//故kotlin里面引入函数式类/对象 作为形参/实参引入
// to lambda
class MyOnClickListener(val listener: (view: View) -> Unit){
}
由此可以得出结论当作为形参传入的时候需要说明函数式类的参数和返回值
实参/传参(函数式对象)简化过程:
//实例化上面的MyOnClickListener类
//思路1: lambda是否是最后一个参数,是 -> 将lambda表达式(即函数式对象)移动到括号外面
MyOnClickListener(){}
//思路2: lambda是否是唯一的一个实参, 是 -> 去掉()
MyOnClickListener{}
//思路3: (view: View) -> Unit 将括号()变为大括号{},并把其他东西统统消失得到, {view: View}, 因为是本质是实际的对象有函数体, 所以加上函数体为 {view: View -> ...(函数体)}
//lambda会根据最后一行自动推断出返回值类型是否为unit,形参已经声明了,所以知道你的返回值是unit,得到
MyOnClickLiintener{ view: View -> 函数体}
//思路4: 是否使用到view没用到就删除,用到就使用it代替,kt可以自动推断
MyOnClickLiintener{ 函数体}//未使用到
MyOnClickLiintener{ use(it)}//使用到, kt会自动推断出it是View类型参数
4
一:什么是lambda表达式
lambda表达式:可以用来表示函数的语法糖,本质是一个匿名函数。
(参数) -> 表达式
二:匿名函数
匿名内部类也就是没有名字的内部类
匿名函数与普通函数基本相似,只是将普通函数的函数名去掉而已。
正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写
三:为什么用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 : 类型, ... ) -> 表达式返回类型){
...
}
第一个参数名是test函数的参数名, 而参数1,参数2是lanbda表达式的参数名
- 无参数的情况
// 源代码
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}
// 调用
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
}
// 调用
test(10,sum(3,5)) // 结果为:18
// lambda
fun test(a : Int , b : (num1 : Int , num2 : Int) -> Int) : Int{
return a + b.invoke(3,5)
}
// 调用
test(10,{ num1: Int, num2: Int -> num1 + num2 }) // 结果为:18