持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情
本篇文章介绍两个kotlin中经常使用到的小技巧:
数据类与普通类的选择
、探究kotlin类委托背后事
。
历史文章
数据类与普通类的选择
数据类就是data class
,这个和普通的class
声明的类有什么区别呢,我们通过反编译成的java代码了解一下。
定义一个数据类Demo1
和普通类Demo2
:
data class Demo1(val content: String = "") {
}
class Demo2 {
val content: String = ""
}
先看下Demo1
反编译成的java代码:
再看下Demo2
反编译成的java代码:
可以很明显的看到,Demo1
这个数据类编译器额外给我们生成了很多的方法,比如提供了解构方法componentX()
、复制方法copy()
等方法,重写了equal()
、hashCode()
等方法。
所以这里我们做一个结论:
如果你想要实现一个普通类封装几个属性而已,不需要复制、比较等操作该属性对象,那就直接使用普通类声明即可;
如果你想要使用这些方法,并且方法的实现和data class
默认提供或者重写的方法实现逻辑相同,那就可以考虑使用data class
声明类;
所以不是每次都要优先使用data class
,它会额外声明一些方法,额外重写一些方法,需要结合具体的业务场景选择性使用。
探究kotlin类委托背后事
现在有这样的一个类委托简单实现:
interface JKWonderful {
fun look() {
println("JKWonderful: look")
}
}
class JKChild(private val jkWonderful: JKWonderful) : JKWonderful by jkWonderful {
override fun look() {
super.look()
}
}
子类实现JKWonderful
接口并将接口实现委托给构造参数jkWonderful
对象。接下来我们反编译成java代码看下背后的实现原理:
public final class JKChild implements JKWonderful {
private final JKWonderful jkWonderful;
public void look() {
JKWonderful.DefaultImpls.look(this);
}
public JKChild(@NotNull JKWonderful jkWonderful) {
Intrinsics.checkNotNullParameter(jkWonderful, "jkWonderful");
super();
this.jkWonderful = jkWonderful;
}
}
可以看到look()
方法直接调用了JKWonderful.DefaultImpls.look(this)
,并没有使用构造参数传入的委托对象的look()
方法,这里请注意下。
接下来我们换一种写法,JKChild
子类不重写方法look()
,如下:
class JKChild(private val jkWonderful: JKWonderful) : JKWonderful by jkWonderful {
override fun look() {
super.look()
}
}
我们反编译成java代码看一下:
可以看到,这次JKChild
类的方法look()
调用了构造参数传入的委托对象的look()
方法。
这就产生了一个问题:
对于第一种情况,子类在委托JKWonderful接口实现给其他对象时,如果额外重写了方法,那么以后该方法的调用将不会再触发委托对象对应方法的调用,这样你这个
委托的作用就完全没有意义
了;
请大家在使用属性委托的时候,一定要谨慎对待,是否真的需要委托还是就单纯的自身重写方法,请根据具体的业务情况具体分析。