hi 大家好,我是 DHL。公众号:ByteCode ,专注分享最新技术原创文章,涉及 Kotlin、Jetpack、算法动画、数据结构 、系统源码、 LeetCode / 剑指 Offer / 多线程 / 国内外大厂算法题 等等。
在之前的文章中,分析过 Kotlin 1.5 宣布了一个重磅特性 value class 这是一个非常实用的特性,提高代码的可读性同时,还可以提高性能,因为编译器会对它进行更深层次的优化。主要包含了以下内容,没有看过的小伙伴可以前去查看 。
inline class
和value class
有什么区别value class
不能被继承,但是可以实现接口- 当传递的对象为空时,
value class
将会失去内联效果 value class
禁止使用===
可以使用==
而今天这篇文章主要介绍 value class
和 data class
的区别,这可能是平时在做业务开发的时候,容易被忽视的几个细节。通过这篇文章,你将学习到以下内容。
- 什么是
value class
? - 什么是
data class
? value class
和data class
的区别
value class
占用更少的内存,执行效率更高value class
执行效率比data class
快多少value class
没有copy()
方法value class
构造函数只能传入一个参数value class
为什么不能重写equals()
、hashcode()
方法value class
和data class
都不能被继承
什么是 value class
value class
表示内联类,需要在主构造函数中传入一个参数,而且需要用 val
进行修饰, 编译成 Java 代码之后,会替换为传进去的值,代码如下所示。
@JvmInline value class User(val name: String) fun login(user: User?): String = user?.name ?: "" fun testInline() { println(login(User("DHL"))) } // 编译后的代码 public static final String login_js0Jwf8/* $FF was: login-js0Jwf8*/(@Nullable String user) { // ...... return var10000; } public static final void testInline() { String var0 = login-js0Jwf8("DHL"); System.out.println(var0); }
正如你所见,编译后的 Java 代码并没有创建额外的对象,而是将在 Kotlin 中创建的对象 User
替换为传进去的值 DHL
。
什么是 data class
data class
表示数据类,编译器会根据主构造函数声明的参数,自动生成 quals()
、 hashCode()
、 toString()
、 componentN()
、 copy()
、 setXXX()
、 getXXX()
等等模板方法,将我们从重复的劳动力解放出来了,专注于核心业务的实现。
data class User(val name: String) // 编译之后 public final class User { private final String name; public final String getName() { return this.name; } ...... public final String component1() { return this.name; } public final User copy(@NotNull String name) { return new User(name); } ...... }
value class 和 data class 的区别
Value Class 占用更少的内存,执行效率更高
为了保证相同的逻辑应用在各个地方,通常我们对于 model 中参数的验证都会封装在当前 model 中,比如在一个 data class User
中检查用户名是否为空。
data class User(var name: String? = null) { init { requireNotNull(name) { "name is not null" } } }
当我们每次创建 User
对象的时候,都会在堆中分配对象,需要占用更多的内存,同时也会使我们的代码执行效率更低。因为对象创建过程是非常的慢。会经历两个过程:类加载过程、对象创建过程。
- 类加载过程
- 会先判断这个类是否已经初始化,如果没有初始化,会执行类的加载过程
- 类的加载过程:加载、验证、准备、解析、初始化等等阶段,之后会执行
<clinit>()
方法,初始化静态变量,执行静态代码块等等
- 对象创建过程
- 如果类已经初始化了,直接执行对象的创建过程
- 对象的创建过程:在堆内存中开辟一块空间,给开辟空间分配一个地址,之后执行初始化,会执行
<init>()
方法,初始化普通变量,调用普通代码块
value calss
的出现很好的帮助我们解决了这些问题,它使代码执行效率更高,占用更少的内存。这全都得益于 Kotlin 编译器对它进行大量的优化。
@JvmInline value class User(val name: String) fun login(user: User?): String = user?.name ?: "" println(login(User("DHL"))) // 编译后的代码 String var0 = login-js0Jwf8("DHL"); System.out.println(var0);
正如你所见当我们在实例化 User
的时候,并没有在堆中分配对象,而是将传递给方法 login()
的参数 User
替换为传进去的值 DHL
。
Value class 执行时间比 data class 快多少
接下来我们用一个例子来感受一下 value class
比 data class
快多少,代码如下所示。
data class User1(val name: String) fun printDataClass(user: User1) {} @JvmInline value class User2(val name: String) fun printValueClass(user: User2) {} @OptIn(ExperimentalTime::class) fun main() { // data class val measureDataClass = measureTime { repeat(100) { User1("DHL") } } println("measure data class time ${measureDataClass.toDouble(TimeUnit.MILLISECONDS)} ms") // value class val measureRunValueClass = measureTime { repeat(100) { User2("DHL") } } println("measure value class time ${measureRunValueClass.toDouble(TimeUnit.MILLISECONDS)} ms") }
上述代码唯一的区别 User1
用 data class
来声明的,User2
用 value class
来声明的,最后的执行时间如下所示。
measure data class time 6.790241 ms measure value class time 0.832866 ms
value class
执行效率远远高于 data class
。当数据量很大时,它们的差距也会越来越大。
Value class 没有 copy () 方法
value calss
同 data class
一样可以有 init function
,也可以有 internal function
,方便我们封装业务逻辑。
但是 value calss
不会生成 copy()
方法,而 data class
编译后会生成 copy()
方法,如下所示。
data class User(val name: String, val pwd: String) // 编译之后 public final class User { ...... @NotNull public final User copy(@NotNull String name, @NotNull String pwd) { return new User(name, pwd); } ...... }
这也意味着通过 data calss
创建对象实例副本,我们不需要重写所有的参数,可以指定需要改变的参数。
user = user.copy(name = "hi-dhl")
而 value calss
只能通过构造函数去创建对象,需要显示指定所有的参数。
Value class 构造函数只能传入一个参数
现阶段 value class
只能在构造函数中传入一个参数,而且需要用 val
进行修饰,而 data calss
支持在构造函数中添加多个参数,参数可以用 val
或者 var
声明。不过在不久的将来 Kotlin 将会支持在 value class
构造函数中添加多个参数,如下图所示。
Value class 和 data class 都不能被继承
因为 value class
和 data class
编译后将会添加 fianl
修饰符,因此不能被继承,同样也不能继承其他的类,如下图所示。
Value class 不能重写 equals () 、hashcode () 方法
value class
相比于 data class
不能重写 equals()
和 hashcode()
方法, 如下图所示。
equals()
方法用于比较两个参数的内容是否相同,关于 Kotlin 中的 ==
和 ===
以及 eauals
方法的区别,可以查看我另外一篇文章 解密 Koltin 中的 == 和 === 以及 eauals。
因为 value class
构造函数只能传入一个参数,而且必须用 val
进行修饰,所以不存在需要比较两个相同的参数场景,因此 Kotlin 不让重写 equals()
和 hashcode()
方法。
如果有帮助点个赞就是对我最大的鼓励
代码不止,文章不停
欢迎关注公众号:ByteCode,持续分享最新的技术
最后推荐长期更新和维护的项目:
- 个人博客,将所有文章进行分类,欢迎前去查看 hi-dhl.com
- KtKit 小巧而实用,用 Kotlin 语言编写的工具库,欢迎前去查看 KtKit
- 计划建立一个最全、最新的 AndroidX Jetpack 相关组件的实战项目以及相关组件原理分析文章,正在逐渐增加 Jetpack 新成员,仓库持续更新,欢迎前去查看 AndroidX-Jetpack-Practice
- LeetCode / 剑指 offer / 国内外大厂面试题 / 多线程题解,语言 Java 和 kotlin,包含多种解法、解题思路、时间复杂度、空间复杂度分析
近期必读热门文章
- 1分钟发布一个网站,不用域名、服务器,网站发布从未如此简单
- 这是最棒的效率工具集,打通 Notion x 云盘 x 其他笔记软件,写作、设计、开发都会用到工具
- Android 12 已来,你的 App 崩溃了吗?
- Kotlin 宣布一个重磅特性
- Google 宣布废弃 LiveData.observe 方法
- 使用 kotlin 需要注意的一个细节
- 影响性能的 Kotlin 代码(一)
- Jetpack Splashscreen 解析 | 助力新生代 IT 农民工 事半功倍
- 为数不多的人知道的 Kotlin 技巧及解析(三)
- 揭秘 Kotlin 中的 == 和 ===
- Kotlin 密封类进化了