咱们好,本次的文章主要是聊聊Kotlin1.5和1.6版别供给的一些特性,希望能扩充咱们的视野,假如能对你日常的开发带来协助,这将是我写这篇文章最大的价值。
一. 挂起函数类型能够作为父类型
在了解这个之前,先看下函数类型做父类型的用法吧:
class Block : () -> String {
override fun invoke(): String {
return ""
}
}
fun test(list: MutableList<() -> String>) {
list.add(Block())
}
Block
类的父类型便是() -> String
,而且需要重写办法invoke()
办法。当需要调用该类时,能够经过Block().invoke()
或者 Block()()
完成。
咱们将上面的反编译成java看下完成原理:

其实本质上便是完成了Function0
这个接口,就那么朴实无华:

假如说咱们充任父类型的函数类型为带一个参数的(Int) -> String
,那么将会是完成Function1
这个接口;以此类推,函数类型每新增一个参数,完成的接口FunctionX中X就要加一,当时这个不能无限新增,现在看到的FunctionX类型的接口最高为Function22
。
咱们能够看下LeakCanary
的源码FragmentAndViewModelWatcher
,就有用到这个特性:


以上都是一般函数类型作为父类型的解说,接下来就看下挂起函数类型作为父类型吧,无非便是一般在函数类型前面添加了一个suspend
要害字。
class Block : suspend () -> String {
override suspend fun invoke(): String {
return withContext(Dispatchers.IO) {
//模拟耗时
delay(2000)
"result"
}
}
}
这样咱们就能够愉快的在invoke
办法中调用协程官方api履行一些比方耗时操作了,十分的好用。
发编译下代码,其实能够看到本质上还是完成了Function1
接口,为什么挂起来的函数类型明明没有参数,为啥不是完成Function0
接口呢?咱们看下反编译的代码:

invoke办法会默认新增一个Continuation
类型的参数,这个是协助咱们履行挂起函数类型的要害,所以最终完成的接口为Function1
。
至于挂起的原理,比方上图invoke办法中详细的代码逻辑太杂乱了,想要了解协程挂起原理的给咱们推荐一篇文章阅览:写给Android工程师的协程指南。
想要运用这个特性,要么你的kotlin插件版别升级到了1.6.0及以上,要么你的插件版别为1.5.30而且在build.gradle脚本中添加装备:

二. 安稳的挂起类型转化
在kotlin1.6.0的版别,完成了安稳的一般函数类型到挂起函数类型的隐式转化,说了这么多,接下来咱们经过一个比方进行了解:
fun execute(block: suspend () -> Unit) {
}
fun test(block: () -> Unit) {
execute(block)
}
首要咱们声明晰一个execute()
办法,其间办法的参数为挂起函数类型;然后test()
办法参数声明晰一个一般函数类型;最后咱们在test()
办法中成功调用了execute()
办法并将一般函数类型的参数传递给了execute()
办法。
上面这就完成了一次一般函数类型到挂起函数类型的转化,请留意,这个操作可不能反着来,也便是由挂起函数类型转化成一般函数类型是不可能的,毕竟一般函数类型可没有协程代码的履行环境。
官方供给这个特性的目的在笔者看来主要是添加兼容性吧,而且咱们不要以为这样转化会添加挂起的开支,毕竟:
由于一般函数类型根本不存在所谓的挂起点,即便转化成挂起函数类型且被履行,也不会履行任何挂起操作的,仅仅作为一个一般的函数履行并回来罢了。
三. 注解类能够被实例化
咱们看下java中一个比方:
定义一个注解HuiHui
:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HuiHui {
String content();
}
这个注解本质上便是一个接口,自然在java中是能够被完成的:
public class HuiImpl implements HuiHui {
@Override
public String content() {
return null;
}
@Override
public Class<? extends Annotation> annotationType() {
return null;
}
}
上面的场景在java中是能够的,那咱们看下kotlin中的注解可不能够这样搞:

报错了看到了不,提示注解类HuiPlus
需要添加open
,可是给其添加open
就会提示:

除了上面这种写法,这样也会报错的:

总之一句话,kotlin中的注解不支撑实例化。Kotlin官方就留意到了这点,这样今后还能和java之间愉快的游玩嘛,所以在1.5.30插件版别出手了。

简略一句话,注解类支撑被实例化,接下来咱们经过代码进行实战。

能够看到上面的代码没有丝毫报错。其实咱们是能够经过kotlin的反射库是能够调用注解类的结构办法的,可是这样显得繁琐杂乱,直接供给一个实例化注解类的特新岂不是愈加美哉?
一般假如咱们想了解kotlin是怎么完成这种才能的,都会尝试反编译成java代码看下。但惋惜的是,对于kotlin注解实例化的细节被躲藏掉了,反编译成java也没有协助,躲藏掉的优点是答应编译器做一些隐式的优化工作。
假如想要运用这个特性,和参阅上面第一节内容末尾的操作。
四. 支撑对类泛型声明注解
解说起来很麻烦,直接给咱们上代码:
@Target(AnnotationTarget.TYPE_PARAMETER)
annotation class FLAG
class Annotation2<@FLAG T> {
val content: String = ""
}
能够看到我声明晰一个FLAG
注解,而且经过@Target(AnnotationTarget.TYPE_PARAMETER)
约束只能给泛型类型润饰。
这个特性相当于把咱们注解的范围愈加颗粒化了,答应咱们以在愈加细致的维度中去处理问题。而且,请留意,这个注解最终是会被编译到jvm字节码的,所以经过Android自定义注解处理器是能够拿到并处理该注解的。 这样就愈加扩展了该特性的运用范围。
假如想要运用这个特性,和参阅上面第一节内容末尾的操作。
五. 长时间支撑对kotlin插件旧版别的保护

从kotlin1.6.0开始,kotlin官方将长时间支撑保护对当时安稳版别的过去三个旧版别的保护,换句话说,假如当时安稳版别为1.6.0,那么对于kotlin1.3、kotlin1.4和kotlin1.5也是继续进行保护的。
六. 总结
本篇文章主要是对kotlin1.5和1.6版别新增的特性做了一些解说,小而美的那种。我这里并没有列举全所有新增的特性,那样对于咱们和我都是一个负担,仅仅拿出一些比较典型的特性共享给咱们。
请留意,假如由某个特性十分适合打开讲讲,以及对于咱们开发很有协助的话,比方@Jvm-default=all、@BuilderInference等等这些特性,我一般都是专门出一篇文章针对这些特性进行解说的,咱们感兴趣能够看下下面的历史文章列表。
七. 历史文章
@JvmDefaultWithCompatibility优化小技巧,了解一下~
优化@BuilderInference注解,Kotlin高版别下了这些“毒手”!
kotlin密封sealed class/interface的迭代之旅
八. 参阅列表
kotlinlang.org/docs/whatsn…
kotlinlang.org/docs/whatsn…
hub.nuaa.cf/Kotlin/KEEP…
youtrack.jetbrains.com/issue/KT-25…
本文正在参与「金石方案」