咱们好,之前有聊过kotlin1.5、1.6版别供给的一些新特性,下面是总结的相关系列文章:
聊聊kotlin1.5和1.6版别供给的一些新特性
kotlin密封sealed class/interface的迭代之旅
优化@BuilderInference注解,Kotlin高版别下了这些“棘手”!
@JvmDefaultWithCompatibility优化小技巧,了解一下~
接下来就带咱们了解kotlin1.7版别供给了哪些特性!
一. 内联类支撑借助内联特点完成类托付
之前有写过一篇文章带咱们了解内联类是啥:value class 用的爽不爽,进来瞧一瞧吧。这里再简单对内联类做个介绍:
- 像内联函数都带有一个内联界说,内联类主要是用来包装特点的,实际上对特点读写访问时,并不会真实创立内联类,这样就协助咱们减少了类创立带来的开销;
- 其次,内联类想要真的不被创立,前提是不能产生装箱(简单理解为:被当作其他类进行运用),比方参与泛型类型传递等,一旦产生装箱内联类就会被真的创立,这样就违反了内联类本身被官方规划的初衷;
咱们看一个内联类完成类托付署理的比方:
interface Bar {
fun foo() = "foo"
}
@JvmInline
value class BarWrapper(val bar: Bar): Bar by object: Bar {}
其间内联类BarWrapper
的Bar
接口完成署理给了object: Bar {}
,这样写是没啥问题的。可是假如你将内联类BarWrapper
的Bar
接口完成署理给了结构特点bar
:
@JvmInline
value class BarWrapper(val bar: Bar): Bar by bar
上面的写法就会报错了,内联类不支撑这种特性。
由于类接口托付给结构特点完成,是类托付常用的一种开发技巧,内联类不支撑这种特性肯定是十分不便利了,所以官方在kotlin1.7.0支撑了这个特性,上面的代码运用起来就不会报错:
二. 类型参数支撑下划线运算符_
简而言之,这个下划线运算符_便是为了协助咱们简化泛型类型揣度,先写一段测验代码:
interface Action<T> {
fun execute(): T
}
class SimpleAction : Action<String> {
override fun execute(): String = "SimpleAction"
}
class ComplexAction : Action<Int> {
override fun execute(): Int = "ComplexAction".length
}
fun <T, S: Action<T>> update() {
}
界说了Action
接口以及两个接口完成类SimpleAction
、ComplexAction
,以及测验办法update()
,能够看到,这个测验办法界说了两个泛型T
和S
,其间S
的泛型类型还得通过T
决议。其实只要咱们声明了S的泛型类型,T的泛型类型自然也就被决议了。可是在代码中,咱们仍是得分别声明S
和T
的泛型类型:
fun main() {
update<String, SimpleAction>()
}
其实当咱们指定了S泛型的详细类型为SimpleAction,T的泛型类型肯定是String了,可是咱们仍是得显现声明。
等到了kotlin1.7.0,官方就为类型参数引入了下划线操作符_,这样编译器会依据上下文主动协助咱们揣度泛型类型:
fun main() {
update<_, SimpleAction>()
}
其实,咱们经常运用的函数类型参数也引入了下划线操作符,当咱们不运用函数参数时,能够运用_代替:
fun main() {
update { _, age ->
"$age"
}
}
fun update(bar: (content: String, age: Int) -> String) {
}
三. 绝不行为null的泛型类型支撑
这个特性其实在kotlin1.6就支撑了,只不过属于测验特性,到了1.7就安稳了下来,可用于生产环境。
相信咱们肯定知道,一般咱们声明的泛型类型参数,是能够传入null的:
fun test200() {
val res = elvisLike(null, 855)
}
fun <T> elvisLike(x: T, y: T): T = x ?: y
假如咱们想要支撑传入的泛型类型制止为null,能够这样写:
这样当咱们传入null就会报错。
但假如我想要完成elvisLike
办法x
参数是能够传null的,y
制止传null,该怎么完成呢,一般能够通过下面这样完成:
可是上面这样写起来显得略微复杂,又是强制指定T的上界为Any代表不为null,这样会影响一切运用T声明的参数类型不行传null,所以还得单独指定x参数的类型为T?
表示可null,外部能够传入null值。
kotlin1.7.0供给了 & Any
语法能够强制指定泛型类型参数不行传为null:
fun test200() {
val res = elvisLike(null, 855)
}
fun <T> elvisLike(x: T, y: T & Any): T & Any = x ?: y
当咱们尝试给y
参数传null,就会报错:
相当的便利好用,咱们能够反编译成java代码看下完成原理:
便是给参数y
添加了参数是否为null校验。
四. 安稳的函数式接口结构引用
这个特性其实也是1.6供给的,只不过1.7才变成安稳版。
这个特性很简单,咱们直接上代码:
fun interface Printer {
fun print(): String
}
fun main() {
val c = ::Printer
val printer: Printer = c.invoke {
"aaa"
}
}
通过::Printer
咱们就拿到了Printer
接口的结构函数实例,调用其invoke办法就能创立一个完成Printer接口的对象。
假如要运用这个特性,需要添加一个compiler option: -XXLanguage:+KotlinFunInterfaceConstructorReference
。
五. 移除JVM的方针版别1.6
也便是说当kotlin插件升级到1.7.0及以上,jvm target需要指定为1.8,而不是1.6.
六. 深递归DeepRecursiveFunction
支撑
咱们或许都碰到过,写一个递归函数处理一些逻辑核算的问题,但假如递归的层数过深,就会存在StackOverflowError
的危险,但递归的层数多深才能形成这种危险,很难给一个确切的答案,关于常见的可预见的有限的递归深度咱们或许能够进行一个测验衡量作用,但总归太费事,所以kotlin供给了DeepRecursiveFunction
递归类支撑。
接下来咱们写一个遍历二叉树深度的比方:
class Tree(val left: Tree?, val right: Tree?)
fun listTree(tree: Tree?): Int {
if (tree == null) return 0
val left = listTree(tree.left)
val right = listTree(tree.right)
return max(left, right) + 1
}
但二叉树或许很深,递归遍历是存在必定危险的,比方二叉树深度上千次,比较极端的状况或许就会触发StackOverflowError
。
所以kotlin1.4就供给了DeepRecursiveFunction
递归类尽或许避免上面的危险,到了kotlin1.7这个API才变得安稳下来。
接下来咱们运用DeepRecursiveFunction
改造下上面代码:
fun testDeepRecursiveFunction(tree: Tree?) {
val drf = DeepRecursiveFunction<Tree?, Int> { t ->
if (t == null) 0 else maxOf(
callRecursive(t.left),
callRecursive(t.right)
) + 1
}
val depth = drf.invoke(tree)
}
其间,callRecursive()
也是调配DeepRecursiveFunction
运用的官方办法。
依据官方文档的说法,运用了DeepRecursiveFunction
,即便上面的二叉树产生了100000递归遍历,也不会产生StackOverflowError
,能够说是相当的牛逼了 。
一起官方推荐:
假如你的递归次数超越额1000次,就应该考虑运用DeepRecursiveFunction
了。
七. 总结
其实我上面介绍的特性大部分属于kotlin语言方面供给的通用新特性,包括针对特定的kotlin/jvm新增的特性。可是关于官方规范库的特性并没有做深化的了解,所以文章中就只提到了一个DeepRecursiveFunction
,其他的规范库特性感爱好的能够看下下面的参阅链接。
八. 参阅链接
kotlinlang.org/docs/whatsn…
本文正在参与「金石方案」