解决的问题
有时候需要将原生数据类型或String类型的参数通过构造封装在一个类中,然后提供一些方法对参数进行操作,使其使用起来成为一个强类型来提高代码健壮性。而一般我们写代码只是传递原生数据类型或String,JVM 会针对原生数据类型或String进行优化,但不会针对包装类做任何处理,使用包装类会创建对象意味着性能的损耗(内存占用,运行效率,GC回收)。
value class
就是实际没有创建class,直接将参数带入function里面了。因为实质上没有class,所以class相关高级特性都不能用。
- 在Kotlin v1.3 推出的 inline class 被废弃,转而使用 v1.5 推出的 value class 会得到更多的优化,对类添加 @JvmInline 注解即可。
- 只能在主构造中声明一个
val
参数,传入参数为 null 会失去内联效果(除了String,因为原生数据类型无法转为 null。)。没有 init{} 代码块,属性不能有幕后字段(没有延迟初始化、属性委托)。 - 不能继承和被继承,可以实现接口但会失去内联效果。必须在顶层声明,枚举类、嵌套类、内部类都不能内联。
- 反编译成 Java 代码并没有创建额外的对象,在 Kotlin 中创建的包装类实例被替换为构造中传进去的值。
- 编译后的函数名称会被打乱,防止方法重载冲突(因为被替换成原生数据类型或String,若存在同名函数会导致参数列表一样)。当出现冲突使得 Java 无法调用
形参接收内联类的函数(因为包装类型被替换成),对函数添加 @JvmName("XXX") 注解另行指定名称即可。 - 可以使用 == 比较内容,无法使用 === 比较引用(既可以表示为基础类型或String,又可以表示为包装类类型,无意义)。
- 受多态和泛型的影响,不一定会获得优化。在运行时被当成另一种类型(父类型,协变?)使用的时候会装箱(当做Any或者实现的接口类型),泛型的类型擦除也会产生装箱,在静态分析中被认为仅当做本身类型使用的时候会拆箱(当做基础类型或者String使用)。
@JvmInline
value class User(val name: String)
对比 typealias
typealies | value class |
只是取别名,使用起来一样 | 包装类,使用存在拆装箱操作 |
与原数据类型相同,同类型可以相互传参赋值 | 强类型,提供类型安全 |