持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
本篇文章主要讲解Kotlin中一个比较实用的功能:value class,希望能给你带来帮助
一. 案例场景
先看一个基本的操作,某个方法(无返回值
)可以实现对String类型的内容进行修改,我们需要调用这个方法传入当前内容,调用完这个方法后拿到修改后的内容,这个时候一般会定义一个包装类
进行实现:
class Wrapper(var name: String? = null)
fun modifyContent(content: Wrapper) {
if (content.name.isNullOrEmpty()) {
content.name = "hahaha"
}
}
应该不会有哪个大哥直接通过modifyContent()
方法传入name
参数去修改,首先这个修改肯定不会生效的,其次kotlin的方法的参数类型都是val
,是无法被修改的。
上面这种写法就产生了一个问题,为了修改这个变量,我们多创建了一个包装类,带来了性能的开销,而value class
就是为了解决这个问题的。
value class
创建的包装类在满足一定的条件后,包装类不会被真正的生成,最终的还是使用的原始包装的变量对象。
接下来我们通过反编译成java代码和字节码两种方式对比下普通类和内联类的区别:
二. 对比分析反编译的java代码和字节码
- 反编译后的java代码
很明显,普通类Wrapper
直接创建了一个类对象,并调用了方法modifyContent()
方法传入该对象;
而内联类Wrapper2
首先调用了一个额外生成的方法constructor-impl()
:
就是原样返回了传入的String类型参数aaa
,所以并没有真正创建Wrapper2
对象,之后调用了modifyContent2-L3ORUUQ()
:
这个额外生成的方法就是对原来方法modifyContent()
的改版,就传入的参数改成了String类型并在方法中使用。
- 编译后的字节码
-
wrapper
对象的创建使用New
命令和INVOKESPECIAL
命令创建了Wrapper
新对象并调用了其给构造方法。 -
wrapper2
对象的创建使用没有创建
Wrapper2
对象,而是和上面分析的反编译后的java代码一样,调用了constructor-impl()
方法等等。
三. 小结
通过上面的java代码和字节码分析,
内联类帮助我们少创建了一个包装类对象
,减少了其带来的性能开销,非常的有用。
四. value class
能替换 typealias
吗?
总说周知,typealias
是用来起别名的,比如我们给函数类型起个别名:
typealias Callback = () -> String
而value class
是用来声明包装变量的类,其实有点类似给变量起个别名,毕竟最终的包装类不会被真正生成。一定场景下我们是可以代替typealias
的。
五. 注意事项
内联类只有被当作自身类使用的时候,才能避免额外创建对象带来的开销,如果被当作其他类,比如泛型T、基类接口等等,会触发内联类的装箱,这个时候使用就还会额外创建这个具体的内联类
,失去了其本来的意义,使用时一定要注意。