Kotlin开胃的几个小技巧,陪你到老(一)

531 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情

本篇文章介绍两个kotlin中经常使用到的小技巧:数据类与普通类的选择探究kotlin类委托背后事

历史文章

你可能需要了解下的Android开发技巧(一)

你可能需要了解下的Android开发技巧(二)

你可能需要了解下的Android开发技巧(三)

Kotlin开胃的几个小技巧,陪你到老(二)

数据类与普通类的选择

数据类就是data class,这个和普通的class声明的类有什么区别呢,我们通过反编译成的java代码了解一下。

定义一个数据类Demo1和普通类Demo2

data class Demo1(val content: String = "") {
    
}

class Demo2 {
    val content: String = ""
}

先看下Demo1反编译成的java代码:

image.png

再看下Demo2反编译成的java代码:

image.png

可以很明显的看到,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代码看一下:

image.png

可以看到,这次JKChild类的方法look()调用了构造参数传入的委托对象的look()方法。

这就产生了一个问题:

对于第一种情况,子类在委托JKWonderful接口实现给其他对象时,如果额外重写了方法,那么以后该方法的调用将不会再触发委托对象对应方法的调用,这样你这个委托的作用就完全没有意义了;

请大家在使用属性委托的时候,一定要谨慎对待,是否真的需要委托还是就单纯的自身重写方法,请根据具体的业务情况具体分析。