关于Kotlin中SAM转换的那些事


Posted in Python onSeptember 15, 2020

前言

随着 Kotlin 1.4 正式发布,关于 SAM 转换的一些问题就可以盖棺定论了。因为这里要讲的都是些旧的东西,所以这是一篇灌水文。

Kotlin对SAM转换的支持情况

在 1.4 发布之前,经常有新人在群里提出关于 SAM 转换的问题。

为了说明这个问题,要分成几个情况来讨论。

我们需要区分这个接口是Java接口还是Kotlin接口:

// 这是Java
interface JavaSome {
 void some(); 
}
// 这是Kotlin
interface KotlinSome {
 fun some()
}

以及区分在Java还是Kotlin里使用该接口:

// 这是Java, ISome是一个接口
void useSome(ISome some) {}
// 这是Kotlin, ISome是一个接口
fun useSome(some: ISome) {}

两两相乘,我们就需要对4种情况进行讨论。当然,useSome 函数都是在 Kotlin 里调用。

1、Java接口,Java使用

// Java
void useSome(JavaSome some) {}
// Kotlin
useSome {} // OK

这种情况下的 SAM 转换,是自古以来 Kotlin 就支持的。

2、Java接口,Kotlin使用

// Kotlin
fun useSome(some: JavaSome) {}

useSome {} // 能否编译成功跟Kotlin版本和编译器参数有关

Kotlin 1.2 以及更旧版本不支持这种情况下的SAM转换。

Kotlin 1.3 版本,Kotlin 官方团队发现他们写的那堆类型推断算法是一座“屎山”,于是重新写了套新的类型推断算法,作为默认关闭的实验性特性加入了 1.3 版本。新的类型推断算法支持这种情况下的SAM转换,不过需要手动传入编译器参数来开启这个功能。

Kotlin 1.4 版本,由于新的类型推断算法已经默认开启,所以这种情况下可以进行SAM转换。

3、Kotlin接口,Kotlin使用

// Kotlin
fun useSome(some: KotlinSome) {}

useSome {} // 编译错误!

这就是广为人知、为人诟病的垃圾 Kotlin 不支持 SAM 转换的情况。

在 Kotlin 1.4 版本,你需要在接口前加上关键字 fun,让它成为一个 fun interface 才能享受到 SAM 转换。

// Kotlin
fun interface KotlinSome {
 fun some()
}

fun useSome(some: KotlinSome) {}

useSome {} // OK

当然 1.3 版本就别想了,老老实实升级吧。

4、Kotlin接口,Java使用

// Java
void useSome(KotlinSome some) {}
// Kotlin
useSome {} // 需要是 fun interface

非常少见。

和上面的第三种情况一样,这需要 Kotlin 1.4 版本的 fun interface 才能进行 SAM 转换。

5、带有suspend函数的Kotlin接口

四天王有五个人不是常识么

fun interface Some {
 suspend fun some()
}

fun useSome(some: KotlinSome) {}

useSome {} // 嘻嘻

在 Kotlin 1.4 的测试版(里程碑版、RC版),可以编译成功,但是运行起来会炸。原因在于 Kotlin 官方团队并没有写好针对这种情况的代码生成(codegen)。于是在 Kotlin 1.4 正式版,他们就 ban 掉了这样的代码,不允许 fun interface 拥有抽象 suspend 函数。

6、一些旧版本的bug

最经典的是那个安卓的LiveData的某个函数:

val liveData = MutableLiveData<Int>()
liveData.observe({ lifecycleOwner.lifecycle }, Observer { invokeMyMethod(it) })
// 第二个参数无法进行SAM转换

详见KT-14984。

新的类型推断算法修正了这个bug。

SAM Constructor

在 1.3 以及更早的版本,针对上面所说的第二种情况,可以这样使用:

// Kotlin
fun useSome(some: JavaSome) {}

useSome(JavaSome {})

想必各位过来人都知道这样的写法。

这里 JavaSome {},lambda 表达式前面的那个 JavaSome 就是所谓的 SAM 构造器(SAM constructor),或者说是 SAM 适配器(SAM adapter)。

在现在 1.4 版本里,SAM constructor 已经没什么用了,主要用途是“凭空捏出”一个 SAM 接口的实例:

val ktSome = KotlinSome {} // 需要是 fun interface
val javaSome = JavaSome {}

// 错误用法
// val ktSome: KotlinSome = {}
// val javaSome: JavaSome = {}

SAM constructor 可以理解为编译器为 SAM 接口生成了一个如下所示的辅助函数,但是实际上这个函数并不存在。

// 这是Java
interface JavaSome {
 void some(); 
}
// 实际上并不存在的辅助函数
inline fun JavaSome(block: () -> Unit): JavaSome {
 return 编译器的魔法
}

然后就有一些鲜为人知的用法,比如说这样:

// Kotlin
val lambda: () -> Unit = { println("test") }
val kepa: JavaSome = JavaSome(lambda) // 嘻嘻
kepa.some() // 输出 test

上面这段代码确实是可以跑的。

甚至是这样:

val lambda: () -> Unit = { println("test") }
val some: KFunction1<() -> Unit, JavaSome> = ::JavaSome // 嘻嘻
val kepa: JavaSome = some.invoke(lambda)
kepa.some()

这段代码 IDEA 不会提示错误,但是会编译失败。

表面上看确实有这个辅助函数,所以这样的代码可以通过 Kotlin 编译器前端的检查。但是实际上编译器的后端并没有办法针对这样的情况进行代码生成,彻底懵逼了,boom!

你学到了什么

  • 一些无用的历史知识
  • 关于 SAM constructor 的冷知识

本文完。

总结

到此这篇关于关于Kotlin中SAM转换的文章就介绍到这了,更多相关Kotlin中SAM转换内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python数据结构与算法之列表(链表,linked list)简单实现
Oct 30 Python
如何在sae中设置django,让sae的工作环境跟本地python环境一致
Nov 21 Python
Python学生成绩管理系统简洁版
Apr 05 Python
使用anaconda的pip安装第三方python包的操作步骤
Jun 11 Python
python aiohttp的使用详解
Jun 20 Python
使用 Python 快速实现 HTTP 和 FTP 服务器的方法
Jul 22 Python
python实现ip地址的包含关系判断
Feb 07 Python
Python 剪绳子的多种思路实现(动态规划和贪心)
Feb 24 Python
利用Python实现某OA系统的自动定位功能
May 27 Python
pycharm全局搜索的具体步骤
Jul 28 Python
Idea安装python显示无SDK问题解决方案
Aug 12 Python
python实现图片素描效果
Sep 26 Python
Python二元算术运算常用方法解析
Sep 15 #Python
Python实现像awk一样分割字符串
Sep 15 #Python
详解Pycharm安装及Django安装配置指南
Sep 15 #Python
Java Unsafe类实现原理及测试代码
Sep 15 #Python
python装饰器实现对异常代码出现进行自动监控的实现方法
Sep 15 #Python
Python requests上传文件实现步骤
Sep 15 #Python
python -v 报错问题的解决方法
Sep 15 #Python
You might like
php结合redis高并发下发帖、发微博的实现方法
2016/12/15 PHP
thinkphp5.1框架模板赋值与变量输出示例
2020/05/25 PHP
判断是否安装flash player及当前版本的JS代码
2013/08/08 Javascript
利用毫秒减值计算时长的js代码
2013/09/22 Javascript
jQuery实现复选框全选/取消全选/反选及获得选择的值
2014/06/12 Javascript
分析了一下JQuery中的extend方法实现原理
2015/02/27 Javascript
自定义Angular指令与jQuery实现的Bootstrap风格数据双向绑定的单选与多选下拉框
2015/12/12 Javascript
jQuery javascript获得网页的高度与宽度的实现代码
2016/04/26 Javascript
利用jQuery来动态为属性添加或者删除属性的简单方法
2016/12/02 Javascript
React Native实现进度条弹框的示例代码
2017/07/17 Javascript
node命令行工具之实现项目工程自动初始化的标准流程
2019/08/12 Javascript
浅析js实现网页截图的两种方式
2019/11/01 Javascript
Vue中rem与postcss-pxtorem的应用详解
2019/11/20 Javascript
[01:02:30]Mineski vs Secret 2019国际邀请赛淘汰赛 败者组 BO3 第三场 8.22
2019/09/05 DOTA
给Python的Django框架下搭建的BLOG添加RSS功能的教程
2015/04/08 Python
详解python实现读取邮件数据并下载附件的实例
2017/08/03 Python
对Python的zip函数妙用,旋转矩阵详解
2018/12/13 Python
Python 类,property属性(简化属性的操作),@property,property()用法示例
2019/10/12 Python
python编写softmax函数、交叉熵函数实例
2020/06/11 Python
Django ORM判断查询结果是否为空,判断django中的orm为空实例
2020/07/09 Python
python+pygame实现坦克大战小游戏的示例代码(可以自定义子弹速度)
2020/08/11 Python
图库照片、免版税图片、矢量艺术、视频片段:Depositphotos
2019/08/02 全球购物
澳大利亚顶级美发和美容贸易超市:glamaCo
2020/01/19 全球购物
Oracle性能调优原则
2012/05/03 面试题
保密承诺书范文
2014/03/27 职场文书
学习雷锋精神演讲稿
2014/05/10 职场文书
环保建议书600字
2014/05/14 职场文书
2014年自愿离婚协议书范本
2014/09/25 职场文书
2014年小班保育员工作总结
2014/12/23 职场文书
上市公司财务总监岗位职责
2015/04/03 职场文书
环保建议书作文400字
2015/09/14 职场文书
90条交通安全宣传标语
2019/10/12 职场文书
浅谈Python3中datetime不同时区转换介绍与踩坑
2021/08/02 Python
详解Python中__new__方法的作用
2022/03/31 Python
python中pycryto实现数据加密
2022/04/29 Python
Win10服务主机占用内存怎么办?Win10服务主机进程占用大量内存解决方法
2022/09/23 数码科技