value class 用的爽不爽,进来瞧一瞧吧

395 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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代码和字节码

image.png
  1. 反编译后的java代码
image.png

很明显,普通类Wrapper直接创建了一个类对象,并调用了方法modifyContent()方法传入该对象;

而内联类Wrapper2首先调用了一个额外生成的方法constructor-impl()

image.png

就是原样返回了传入的String类型参数aaa,所以并没有真正创建Wrapper2对象,之后调用了modifyContent2-L3ORUUQ():

image.png

这个额外生成的方法就是对原来方法modifyContent()的改版,就传入的参数改成了String类型并在方法中使用。

  1. 编译后的字节码
  • wrapper对象的创建使用

    image.png

    New命令和INVOKESPECIAL命令创建了Wrapper新对象并调用了其给构造方法。

  • wrapper2对象的创建使用

    image.png

    没有创建Wrapper2对象,而是和上面分析的反编译后的java代码一样,调用了constructor-impl()方法等等。

三. 小结

通过上面的java代码和字节码分析,内联类帮助我们少创建了一个包装类对象,减少了其带来的性能开销,非常的有用。

四. value class 能替换 typealias 吗?

总说周知,typealias是用来起别名的,比如我们给函数类型起个别名:

typealias Callback = () -> String

value class是用来声明包装变量的类,其实有点类似给变量起个别名,毕竟最终的包装类不会被真正生成。一定场景下我们是可以代替typealias的。

五. 注意事项

内联类只有被当作自身类使用的时候,才能避免额外创建对象带来的开销,如果被当作其他类,比如泛型T、基类接口等等,会触发内联类的装箱,这个时候使用就还会额外创建这个具体的内联类,失去了其本来的意义,使用时一定要注意。