2024五月发布了Kotlin 2.0,包含了一些新特性比如Enhanced Type Inference
、Multiple Context Receivers
、Better Coroutines
等,本文研究下这几个新特性。
1 Improved Type Inference
在Kotlin 1.x版本中,有下面的类型推导写法:
val listOfPairs = listOf(Pair(1, "One"), Pair(2, "Two"))
val mapFromList = listOfPairs.associate { pair: Pair<Int, String> ->
pair.first to pair.second
}
在Kotlin 2.x版本,就可以使用更好的方式:
val listOfPairs = listOf(1 to "One", 2 to "Two")
val mapFromList = listOfPairs.associate { it.first to it.second }
2 Multiple Context Receivers
在Kotlin 2.x版本,支持了传入多个context-receivers参数,首先我们需要了解下什么是context-receivers。
2.1 了解 context-receivers
比如我们要给String写个弹Toast的扩展函数,一般我们会这样写:
fun String.toast1(activity: Activity, duration: Int = Toast.LENGTH_LONG) {
Toast.makeText(activity, this, duration).show()
}
借助context-receivers,我们对上面的函数进行改造,使之更加简洁易用:
第一步, 在app/build.gradle.kts添加配置:
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs += arrayOf("-Xcontext-receivers")
}
如果不指定-Xcontext-receivers
,会有错误提示:The feature "context receivers" is experimental and should be enabled explicitly
。
第二步,新的函数实现:
context(Activity)
fun String.toast2(duration: Int = Toast.LENGTH_LONG) {
Toast.makeText(this@Activity, this, duration).show()
}
这样,我们可以直接在一个Activity的实例中调用"hola amigo".toast2(5000)
,而不必再传Activity参数了,是不是比第一个实现方式有那么一点简洁呢?
context-receivers也挺简单的,下面介绍下Multiple Context Receivers。顾名思义,就是支持传入多个参数。可以看下面的代码:
class Database {
fun queryData(): String = "Data from database"
}
class Network {
fun fetchData(): String = "Data from network"
}
context(Database, Network)
fun getData(): String {
val dbData = queryData()
val networkData = fetchData()
return "$dbData - $networkData"
}
fun testWith() {
val database = Database()
val network = Network()
with(database) {
with(network) {
println(getData())
}
}
}
可以看到,getData
函数支持传两个context-receiver参数,分别是Database和Network。
3 Improved Coroutines
关于协程优化,我们贴出两个功能一模一样的函数代码,只是实现方式有点小diff,一段是Kotlin1.x的实现kotlin1_x()
,一段是Kotlin2.x的实现kotlin2_x()
。
suspend fun preBlockTest(): Int {
StarkLog.i("zzh", "abtestclick preblock start 0000")
delay(300)
StarkLog.i("zzh", "abtestclick preblock end 1111")
return -1
}
suspend fun networkRequest(): Int {
StarkLog.i("zzh", "abtestclick network start 0000")
delay(300)
StarkLog.i("zzh", "abtestclick network end 1111")
return 1
}
suspend fun databaseQuery(): String {
StarkLog.i("zzh", "abtestclick database start 0000")
delay(200)
StarkLog.i("zzh", "abtestclick database end 1111")
return "succ"
}
fun kotlin1_x() {
StarkLog.i("zzh", "abtestclick start 0000")
runBlocking {
var d1: Deferred<Int> = async { preBlockTest() }
d1.invokeOnCompletion { handler ->
run {
StarkLog.i("zzh", "abtestclick preblock complete")
}
}
var d1ret = d1.await() // 这里的await会阻塞下面两个协程的执行,否则不会阻塞,
StarkLog.i("zzh", "abtestclick preblock await d1ret=${d1ret}")
// kotlin 1.x
val deferredResults = listOf(
async { networkRequest() },
async { databaseQuery() }
)
val results = deferredResults.awaitAll()
StarkLog.i("zzh", "abtestclick Results: $results")
}
StarkLog.i("zzh", "abtestclick end 1111")
}
fun kotlin2_x() {
StarkLog.i("zzh", "abtestclick start 0000")
runBlocking {
var d1: Deferred<Int> = async { preBlockTest() }
d1.invokeOnCompletion { handler ->
run {
StarkLog.i("zzh", "abtestclick preblock complete")
}
}
var d1ret = d1.await() // 这里的await会阻塞下面两个协程的执行,否则不会阻塞,
StarkLog.i("zzh", "abtestclick preblock await d1ret=${d1ret}")
// kotlin 2.x
val results2 = awaitAll(
async { networkRequest() },
async { databaseQuery() }
)
println("Results: $results2")
}
StarkLog.i("zzh", "abtestclick end 1111")
}
参考: