您现在的位置是:首页 > 正文

lambda java kotlin,为什么Kotlin调用 java 时可以使用Lambda?

2024-01-31 00:06:19阅读 0

如果 lambda 是一个函数的唯一参数,那么调用这个函数时可以省略圆括号

如果 lambda 所表示的匿名函数只有一个参数,那么可以省略它的声明以及->符号(默认会用it来给省略的参数名命名)

OK,从代码三的结构中,能够更清晰的看出,这里的 view.setOnClickListener 函数是接收了一个 lambda 作为参数。而在 Kotlin 中,什么样的函数才能把lambda(也即另一个函数)作为参数呢?—— 对,就是高阶函数。

什么是高阶函数?

高阶函数是将函数用作参数或返回值的函数。

这是 Kotlin 和 Java 的区别之一,java 中并没有高阶函数的支持(java8是有高阶函数的)。当我们在 java 中需要用到类似的概念时,通常的做法是传递一个匿名类作为参数,然后实现其中的某些抽象方法 —— 就比如上面的代码二。

事实上,如果在 Android Studio 中,从 Kotlin 的代码查看 view.setOnClickListener 函数的定义,就会发现,看到的函数签名就是一个高阶函数的定义:

7fd9d2c9df22919795e548166887c0c2.png

函数签名提示

如上图,所看到函数签名是:

public final fun setOnClickListener(l: ((v:View!)->Unit)!): Unit

当然,因为方法是在 Java 中定义的,所以它也列出了 Java 的声明,是这样:

public void setOnClickListener(OnClickListener l)

我们知道,Kotlin 跟 Java 的很多类型都有差异,所以它们在互相调用的时,会有一个按照对应关系的转换。

对于上面的对 setOnClickListener 方法的转换,别的地方都好理解,比较难懂的是,为什么会把参数从 OnClickListener 类型转换成了 (View) -> Unit。

(View) -> Unit 是一个函数类型,它表示这样一个函数:接收1个View类型的参数,返回Unit。

正是这个对参数类型的转换,使得 setOnClickListener 方法在 Kotlin 中变成了一个高阶函数,这样正是它之所以能够使用 lambda 作为参数的原因。

而这种转换,就是我们题目中所说到这篇文章的主角 —— SAM 转换 (Single Abstract Method Conversions)。

3. 什么是 SAM 转换?

好吧,说了这么多,终于到正题了。

SAM 转换,即 Single Abstract Method Conversions,就是对于只有单个非默认抽象方法接口的转换 —— 对于符合这个条件的接口(称之为 SAM Type ),在 Kotlin 中可以直接用 Lambda 来表示 —— 当然前提是 Lambda 的所表示函数类型能够跟接口的中方法相匹配。

而 OnClickListener 在 java 中的定义是这样的:

// 代码四:OnClickListener 接口在 java 中的定义public interface OnClickListener {    void onClick(View v);

}1234

—— 恰好它就是一个符合条件的 SAM Type,onClick 函数的类型即是 (View) -> Unit。所以,在 Kotlin 中,能够用 lambda 表达式 { println("click")} 来代替 OnClickListener 作为 setOnClickListener 函数的参数。

4. SAM 转换的歧义消除

SAM 转换的存在,使得我们在 Kotlin 中调用 java 的时候能够更得心应手了,它在大部分的时间都能工作的很好。

当然,也偶尔会有例外,比如,考虑下面的这段代码:

// 代码五public class TestSAM {

SamType1 sam1,;

SamType2 sam2,;    public void setSam(SamType1 sam1) {        this.sam1 = sam1;

}    public void setSam(SamType2 sam2) {        this.sam2 = sam2;

}    public interface SamType1 {        void doSomething(int value);

}    public interface SamType2 {        void doSomething2(int value);

}

}123456789101112131415161718

—— TestSAM 有两个重载的 setSam 方法,—— 并且它们的参数( SamType1、SamType2 )都是 SAM Type 的接口。—— 并且 SamType1 跟 SamType2 的唯一抽象方法的函数类型都是 (Int) -> Unit 。

dcce4a492ca07ca2e6aa0860c940bc24.png

o(╯□╰)o

这种情况比较吊轨,但是还有有可能会出现的。这时候,如果在 Kotlin 中直接使用代码一类似的方式,就会报错了:

// 代码六:kotlin中调用,这段代码是编译不过的TestSAM().setSam {

println("dodo")

}1234

会提示这里歧义,编译器不知道这个 Lambda 代表是 SamType1 跟 SamType2 中的哪一个接口。

解决的办法就是手动标明 Lambda 需要代替的接口类型,有两种方式可以来标明:

// 代码七: 歧义消除// 方式一TestSAM().setSam (SamType1 { println("dodo")  })

// 方式二TestSAM().setSam ({ println("dodo") } as SamType1)12345

当然,也还有一种方法是不再使用 SAM 转换的机制,而是直接使用一个 SamType1 的实例作为参数:

// 代码八: 使用一个实现接口的匿名类作为参数TestSAM().setSam(object : TestSAM.SamType1 {    override fun doSomething(value: Int) {

println("dodo")

}

})123456

这种方法当然也是可以的,只是跟 lambda 相比起来,就显得不那么优雅了(优雅很重要!!!)。

5. SAM 转换的限制

SAM 转换的限制主要有两点 :

5.1 只支持 java

即只适用与 Kotlin 中对 java 的调用,而不支持对 Kotlin 的调用

官方的解释是 Kotlin 本身已经有了函数类型和高阶函数等支持,所以不需要了再去转换了。

如果你想使用类似的需要用 lambda 做参数的操作,应该自己去定义需要指定函数类型的高阶函数。

5.2 只支持接口,不支持抽象类。

这个官方没有多做解释。

我想大概是为了避免混乱吧,毕竟如果支持抽象类的话,需要做强转的地方就太多了。而且抽象类本身是允许有很多逻辑代码在内部的,直接简写成一个 Lambda 的话,如果出了问题去定位错误的难度也加大了很多。

6. 总结

OK,讲完了。总结起来就是 SAM 转换就是 kotlin 在调用 java 代码时能使用 Lambda 的原因。了解了其原理,能够让我们在写代码更自如,在偶尔出问题的时候也能更好更快地解决。

网站文章

  • P4556 雨天的尾巴

    题目背景深绘里一直很讨厌雨天。灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。无奈的深绘里和村民们只好等待救济粮来维生。不过救济粮的发放方式很特别。题目描述首先村落里的一共有n座房屋,并形成一个树状结构。然后救济粮分m次发放,每次选择...

    2024-01-31 00:05:50
  • 2.代理模式

    1. 什么是代理模式??作用是什么??有哪几种代理模式??1.1 什么是代理模式代理模式是指,为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而...

    2024-01-31 00:05:42
  • python爬虫思维

    python爬虫思维

    爬虫思维

    2024-01-31 00:05:36
  • 以物联网为核心的智慧工地云平台:聚集智能技术,实现建筑工地智慧管理

    以物联网为核心的智慧工地云平台:聚集智能技术,实现建筑工地智慧管理

    平台与塔吊机的监控主机对接,直接获取监控主机内塔吊机的风速、幅度、倾角、重量、转角、高度、工作等数据,避免超载超速等不安全作业,同时在塔吊机上安装人脸识别设备,通过人脸识别设置司机权限,避免非操作员操...

    2024-01-31 00:04:58
  • 动态内存管理(C语言)

    动态内存管理(C语言)

    一篇文章带你了解动态内存管理(C语言)

    2024-01-31 00:04:51
  • 01背包方案数(变种题)Stone game--The Preliminary Contest for ICPC Asia Shanghai 2019

    题意:https://nanti.jisuanke.com/t/41420给你n个石子的重量,要求满足(Sum<=2*sum<=Sum+min)的方案数,min是你手里的最小值。思路:从最大重量的石子开始背包,每次ans+=dp【j-v【i】】就行了。 1 #define IOS ios_base::sync_with_stdio(0); cin.tie...

    2024-01-31 00:04:21
  • shiro框架配置免单点登录--初学版

    spring shiro 免单点登录

    2024-01-31 00:04:13
  • rsync远程同步

    rsync远程同步

    rsync

    2024-01-31 00:04:05
  • 多线程实现UDP协议发送、接收数据

    package cn.idcast5; import java.io.IOException; import java.net.DatagramSocket; import java.net.SocketException; public class ChatRoom { public static void main(String[] args) throws IOExce...

    2024-01-31 00:03:35
  • 高大上!手把手教你在京东云擎上部署个人应用!

    高大上!手把手教你在京东云擎上部署个人应用!

      楼主这几天在研究微信开发,首先要找一个云平台,恰好上次在博客园见到了一个讲在京东云擎上部署自己网站的文章。作者没有详细写。本文教大家如何在京东云擎上部署自己的应用,并且演示了简单的网站登录操作。京...

    2024-01-31 00:03:28