关于Kotlin中SAM转换的那些事

作者:Dexlind 时间:2022-02-09 15:14:38 

前言

随着 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 的冷知识

本文完。

来源:https://aisia.moe/2020/09/12/kotlin-sam-history/

标签:kotlin,sam,转换
0
投稿

猜你喜欢

  • js表单检测数字

    2007-10-12 13:52:00
  • Python用Try语句捕获异常的实例方法

    2021-07-14 10:28:54
  • 理解JavaScript作用域链

    2010-01-23 11:31:00
  • php基于curl主动推送最新内容给百度收录的方法

    2023-11-22 04:46:44
  • ORACLE 数据库RMAN备份恢复

    2009-04-24 12:23:00
  • oracle指定排序的方法详解

    2023-07-14 15:36:28
  • python学习笔记之调用eval函数出现invalid syntax错误问题

    2023-11-03 01:48:30
  • win2000server IIS和tomcat5多站点配置

    2023-06-26 04:45:33
  • HTML 5 V.S. XHTML 2:HTML5倍受青睐,XHTML2处境尴尬

    2009-06-08 12:57:00
  • 对python requests发送json格式数据的实例详解

    2023-12-11 05:16:07
  • 对网页切片算法问题的研究

    2008-06-05 12:41:00
  • 有效LOGO设计的最重要的提示

    2010-06-09 12:05:00
  • python ConfigParser库的使用及遇到的坑

    2021-08-13 05:45:23
  • 详细介绍查询优化技术在现实系统中的运用

    2009-01-04 13:34:00
  • 常用于后台开发的jQuery插件

    2010-09-25 12:47:00
  • 百度百科中的asp词条:什么是asp

    2008-10-11 14:38:00
  • 理解 SQL Server 中系统表Sysobjects

    2009-01-20 15:13:00
  • Python读取xlsx数据生成图标代码实例

    2022-01-11 11:35:56
  • MySQL最好的优化技巧

    2009-10-27 12:05:00
  • 关于python爬虫应用urllib库作用分析

    2023-11-02 12:59:43
  • asp之家 网络编程 m.aspxhome.com