- 1:内联类
- 1.1:更改Java调用的JVM函数的名称
- 1.2:初始化块
- 1.3:内联值类
- 1.4:Value类
- 2:JVM Record支持
- 3:密封接口和密封类的改进
- 4:如何使用1.5版本新功能
Jetbrains在接下来的Kotlin 1.5中添加新的语言功能,并且可以在Kotlin 1.4.30中试用。主要添加的功能如下:
- 内联类
- JVM Record支持
- 密封接口和密封类的改进
内联类
自Kotlin 1.3就已经提供了内联类的Alpha版本,并在1.4.30中将其升级为Beta。从Kotlin 1.5起稳定了内联类的概念,使它成为更通用的功能的一部分。
有时,业务逻辑有必要围绕某种类型创建包装类。但是,由于额外的堆分配,它引入了运行时开销。而且,如果包装的类型是原始类型,那么性能损失将是很可怕的,因为原始类型通常在运行时进行了严重的优化,而它们的包装类并不会得到任何特殊的处理。
为了解决此类问题,Kotlin引入了一种称为内联类的特殊类。内联类是基于值的类的子集。
inline class Color(val color:Int){
fun printColor(){
print(color)
}
}
fun doSomething(color:Color){
//do something
}
fun main(){
val color = Color(255)
//作为静态函数调用
color.printColor()
doSomething(color)
}
内联类的构造函数只能是一个原始类型或任何引用类型(如String
)。在执行doSomething函数时编译器会用Color中的Int的值替换内联类实例:
int color = Color.constructor-impl(255);
Color.printColor-impl(color);
doSomething-YuIBNpY(color);//调用方法时没有分配额外的对象
更改Java调用的JVM函数的名称
从Kotlin1.4.30开始,可以使用内联类作为参数来更改函数的JVM名称,以使其可从Java使用。可以使用注解@JvmName来修饰函数,它将在字节码中更改此函数的名称,并使其可以从Java调用并直接传递值:
// 定义内联函数
inline class Timeout(val millis: Long)
val Int.millis get() = Timeout(this.toLong())
val Int.seconds get() = Timeout(this * 1000L)
@JvmName("greetAfterTimeoutMillis")
fun greetAfterTimeout(timeout: Timeout)
// Kotlin调用
greetAfterTimeout(2.seconds)
// Java调用
greetAfterTimeoutMillis(2000);
初始化块
Kotlin 1.4.30中内联类的另一个改进是,现在可以在init块中定义初始化逻辑:
inline class Name(val s: String) {
init {
require(s.isNotEmpty())
}
}
内联值类
Kotlin 1.5稳定了内联类的概念,并使它成为更通用的功能的一部分:value class。
到目前为止,内联类已构成一种独立的语言功能,但现在它们已成为具有一个参数的值类的特定JVM优化。值类代表了一个更笼统的概念,将支持不同的优化:现在是内联类,而在项目Valhalla可用时,将来将是Valhalla基本类。
目前唯一是语法。由于内联类是优化的值类,因此必须以与以前不同的方式声明它:
@JvmInline
value class Color(val rgb: Int)
可以使用一个构造函数参数定义一个值类,并使用对其进行注释@JvmInline。我们应该从Kotlin 1.5开始就使用这种新语法。旧语法inline class将继续工作一段时间。在Kotlin 1.5中将有不推荐使用该定义方式的警告。
Value类
一个value类表示具有数据不可变的实体。目前,一个value类只能包含一个属性来支持旧内联类的用例。
在将来的Kotlin版本完全支持此功能,将可以定义具有许多属性的值类别。所有值都应为只读val
:
value class Point(val x: Int, val y: Int)
值类完全由存储的数据定义,并且不允许对其进行===检查。==检查会自动比较基础数据。
JVM Record支持
JDK15中引入了record,JVM生态系统中的另一个即将到来的改进是Java record。它们类似于Kotlin data类,并且主要是简单的数据持有者。
Java record不遵循JavaBeans约定,它们具有x()和y()方法,而不是熟悉的getX()和getY()。
与Java的互操作性一直是Kotlin的并且仍然是优先考虑的事项。因此,Kotlin代码兼容了新的Java record,并将它们视为具有Kotlin属性的类。这类似于遵循JavaBeans约定的常规Java类的工作方式:
// Java
record Point(int x, int y) { }
// Kotlin
fun foo(point: Point) {
point.x // seen as property
point.x() // also works
}
出于互操作性的原因,还可以对data类进行注解:@JvmRecord以生成新的JVM record方法:
@JvmRecord
data class Point(val x:Int,val y:Int)
@JvmRecord注解使编译器生成x()和y()方法,而不是标准getX()和getY()方法。
密封接口和密封类的改进
使类密封时,它将层次结构限制为已定义的子类,从而可以在when分支中进行详尽的检查。在Kotlin 1.4中,密封的类层次结构具有两个约束。首先,顶级类不能是密封的接口,它应该是一个类。其次,所有子类应位于同一文件中。
Kotlin 1.5消除了两个约束:现在可以使接口密封。子类(包括密封类和密封接口)应与父类位于同一编译单元和同一包中,但现在可以位于不同文件中。
sealed interface Expr
data class Const(val number: Double) : Expr
data class Sum(val e1: Expr, val e2: Expr) : Expr
object NotANumber : Expr
fun eval(expr: Expr): Double = when(expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}
如何使用1.5版本新功能
需要使用Kotlin 1.4.30来指定语言版本1.5以启用新功能:
compileKotlin {
kotlinOptions {
languageVersion = "1.5"
apiVersion = "1.5"
}
}