Kotlin基础语法 十二、lamdba表达式解析三(函数类型)

前言

因为在kotlin的官网中,对lamdba的介绍篇幅有限而且没有逻辑, 所以在我最初开始接触kotlin的时候,我对kotlin的lamdba表达式有非常多的困惑,为此我花了接近一天的时间,浏览了网上几十篇的文章,才慢慢总结整理了kotlin的lamdba表达式知识点,其中关于 函数类型 讲的最清楚直白的应该是 扔物线 大神的一篇文章:
【码上开学】Kotlin 的高阶函数、匿名函数和 Lambda 表达式
建议对 Lambda 表达式函数类型迷茫的同学去看一下这篇文章,你会感到豁然开朗。
本篇文章只是我自己顺着大神文章的思路和自己理解简单记录的知识点供自己查看,个人强烈推荐原文章。

一、函数类型的由来

在 Java 里,如果你有一个 a 方法需要调用另一个 b 方法,你可以直接在a方法的方法体里面调用:
示例:

int b(int params) {
    return params;
}
int a() {
    return b(1);
}

但这样我们无法动态的传递参数给b方法,
如果我们想在 a 方法调用时动态设置 b 方法的参数,你就得把参数传给 a方法,再从 a 方法的内部把参数传给 b方法,
示例:

int b(int params) {
    return params;
}

int a(int params) {
    return b(params);
}
a(1); // 内部调用 b(1)
a(2); // 内部调用 b(2)

不过……如果我们想动态设置的不是方法参数,而是方法本身呢?比如我在 a 的内部有一处对某个方法的调用,这个方法可能是 b,可能是 c,不一定是谁,我只知道,我在这里有一个调用,它的参数类型是 int ,返回值类型也是 int ,而具体在 a 执行的时候内部调用哪个方法,我希望可以动态设置,
示例:

int a(??? method) {  
return method(1);
}
a(method1);
a(method2);

但是在 Java 里是不允许把方法作为参数传递的,但是确实有一个变通方案:接口。我们可以通过接口的方式来把方法包装起来,

public interface Wrapper {  
int method(int param);
}

然后把这个接口的类型作为外部方法的参数类型:

int a(Wrapper wrapper) {  
return wrapper.method(1);
}

在调用外部方法时,传递接口的对象来作为参数:

a(wrapper1);
a(wrapper2);

在Android 中最典型的例子就是我们的点击事件:所谓的点击事件,最核心的内容就是调用内部的一个 OnClickListener 的 onClick() 方法:

public interface OnClickListener {  
void onClick(View v);
}

而所谓的这个 OnClickListener 其实只是一个壳,它的核心全在内部那个 onClick() 方法。换句话说,我们传过来一个 OnClickListener:

OnClickListener listener1 = new OnClickListener() {  
@Override  
void onClick(View v) {    
doSomething();  
}
};
view.setOnClickListener(listener1);

本质上其实是传过来一个可以在稍后被调用的方法(onClick())。只不过因为 Java 不允许传递方法,所以我们才把它包进了一个对象里来进行传递。

二、Kotlin的函数类型

在 Kotlin 里面,函数的参数也可以是函数类型的,也就是我们可以传递一个函数(其实是函数的对象)给函数作为参数并在方法体内调用。
形式如下:
fun a(funParam: Fun): String {
return funParam(1);
}
当一个函数含有函数类型的参数的时候,如果你调用它,你就可以——当然你也必须——传入一个函数类型的对象给它,
形式如下:
fun b(param: Int): String {
return param.toString()
}
a(b)
当然,上面的两个代码只是形式,真正的kotlin中并不能这么写。

函数类型:用来声明一个函数参数和返回值形式的 特殊数据类型
声明格式如下:
(参数类型列表) -> 返回值类型

因为函数类型可以有各种各样不同的参数和返回值的类型的搭配,这些搭配就形成了不同的函数类型,所以对于函数类型的参数,你要指明它有几个参数、参数的类型是什么以及返回值类型是什么。
例如,无参数无返回值(() -> Unit)和单 Int 型参数返回 String (Int -> String)是两种不同的类型
示例:

fun a(funParam: (Int) -> String): String {  
return funParam(1)
}

同样的,函数类型不只可以作为函数的参数类型,还可以作为函数的返回值类型

fun c(param: Int): (Int) -> Unit {  
...
}

这种参数或者返回值为函数类型的函数,在 Kotlin 中就被称为高阶函数。
*除了作为函数的参数和返回值类型,你把函数类型赋值给一个变量也是可以的。不过对于一个声明好的函数,不管是你要把它作为参数传递给函数,还是要把它赋值给变量,都得在函数名的左边加上双冒号才行:
a(::b)
val d = ::b
*双冒号 ::method 到底是什么?
Kotlin 里「函数可以作为参数」这件事的本质,是函数在 Kotlin 里可以作为对象存在——因为只有对象才能被作为参数传递啊。赋值也是一样道理,只有对象才能被赋值给变量啊。但 Kotlin 的函数本身的性质又决定了它没办法被当做一个对象。那怎么办呢?Kotlin 的选择是,那就创建一个和函数具有相同功能的对象。怎么创建?使用双冒号。
在 Kotlin 里,一个函数名的左边加上双冒号,它就不表示这个函数本身了,而表示一个对象,或者说一个指向对象的引用,但,这个对象可不是函数本身,而是一个和这个函数具有相同功能的对象。

示例:

fun b(number: Int) {
    Log.e("调用了b函数", "----" + number)
}

fun a(number: Int, me: (Int) -> Unit) {
    Log.e("调用了a函数", "----")
    me(number)
}
a(3, ::b)

记住:
双冒号+函数名,表示的不是一个函数,而是一个对象,一个函数类型的对象。

三、匿名函数

要传一个函数类型的参数,或者把一个函数类型的对象赋值给变量,除了用双冒号来拿现成的函数使用,你还可以直接把这个函数挪过来写:
形式如下
a(fun b(param: Int): String {
return param.toString()
});
这种写法的话,函数的名字其实就没用了,所以你可以把它省掉:
a(fun(param: Int): String {
return param.toString()
});
上面两个代码,第一个是不能运行的,因为是一个完整的函数声明,表示的是一个函数,而不是一个对象。而第二个是可以运行的,很神奇,那是因为在kotlin中
一个没有名字的函数其实和 双冒号 表示的意义是一样的,都是函数类型的对象。叫做匿名函数。虽然叫函数但并不是真正的函数而是函数类型的对象。

但我们这么写,会很麻烦并且代码也不美观,所以进一步的简化,就是lamdba表达式的形式了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值