注册

聊一聊Kotlin之data class

Kotlin是由JetBrains开发的针对JVM、Android和浏览器的静态编程语言,是Android的官方语言。Kotlin拥有较多高级而又简洁的语法特性,提升了我们的开发效率,减少了代码量。

在使用 java 的时候,我们在用class定义一个entity时除了写get、set方法(使用Kotlin后省了这部分工作),经常还会重写类的 equalshashCodetoString方法,这些方法往往都是模板化的。在 kotlin 中提供了data class搞定这些模版化代码。

data class与class的区别:

实现方式

  • class类
    class ClassUser(val name: String, var age: Int)
  • data class类
    data class DataClassUser(val name: String, var age: Int)

自动重写toString方法

  • data类的toString方法会打印出属性的值
  • 非data类的toString方法则打印出内存地址
    val classUser = ClassUser("classuser", 18)
    val dataClassUser = DataClassUser("dataclassuser", 20)
    println("ClassUser -> ${classUser.toString()}")
    // ClassUser -> com.imock.vicjava.keyuse.ClassUser@11026067

    println("DataClassUser -> ${dataClassUser.toString()}")
    // DataClassUser -> DataClassUser(name=dataclassuser, age=20)

新增componentN方法

  • data类新增属性的componentN方法,component1代表第一个属性,component2代表第二个属性。(常用于解构声明)
    val dataClassUser = DataClassUser("dataclassuser", 20)
    println("DataClassUser component1() -> ${dataClassUser.component1()}")
    // DataClassUser component1() -> dataclassuser

    println("DataClassUser component2() -> ${dataClassUser.component2()}")
    // DataClassUser component2() -> 20

新增copy方法

  • data类新增copy方法,可以用来修改部分属性,但是保持其他不变。
    val dataClassUser = DataClassUser("dataclassuser", 20)
    println("ClassUser toString() -> ${classUser.toString()}")
    // DataClassUser -> DataClassUser(name=dataclassuser, age=20)

    val newDataClassUser = dataClassUser.copy(age = 22)
    println("DataClassUser copy -> ${newDataClassUser.toString()}")
    // DataClassUser copy -> DataClassUser(name=dataclassuser, age=22)

重写hashCode和 equals方法

  • data类重写hashCode方法,equals方法可以稍后看下源码,先判断两个是否是同一个对象,如果不是则进行类型判断,是相同类型则逐个比较属性的值。 
    val classUserLisa1 = ClassUser("lisa", 20)
    val classUserLisa2 = ClassUser("lisa", 20)
    println("ClassUser equals -> ${classUserLisa1.equals(classUserLisa2)}")
    // ClassUser equals -> false
    println("classUserLisa1 hashCode -> ${classUserLisa1.hashCode()}")
    // classUserLisa1 hashCode -> 2081652693
    println("classUserLisa2 hashCode -> ${classUserLisa2.hashCode()}")
    // classUserLisa2 hashCode -> 406765571

    val dataClassUserLisa1 = DataClassUser("lisa", 20)
    val dataClassUserLisa2 = DataClassUser("lisa", 20)
    println("DataClassUser equals -> ${dataClassUserLisa1.equals(dataClassUserLisa2)}")
    // DataClassUser equals -> true
    println("dataClassUserLisa1 hashCode -> ${dataClassUserLisa1.hashCode()}")
    // dataClassUserLisa1 hashCode -> 102981865
    println("dataClassUserLisa2 hashCode -> ${dataClassUserLisa2.hashCode()}")
    // dataClassUserLisa2 hashCode -> 102981865

data class为何如此神奇

data class DataClassUser(val name: String, var age: Int)

class ClassUser(var name: String, var age: Int)

单独看实现上两者没有太大的区别,一个使用data class,一个使用class,为何data class却多出那么多能力?得益于Kotlin高级的语法特性。我们都知道kotlin最终还是要编译成 java class 在 JVM 上运行的,为了更好的理解Kotlin高级而又简洁的语法特性,有时我们需要看看用kotlin写完的代码编译后是什么样子。Talk is cheap, show me the code.

class类编译后的java代码

Kotlin写法如下:

class ClassUser(var name: String, var age: Int)

查看编译后的java代码如下,可以看到帮我们自动生成了get、set和构造方法:

public final class ClassUser {
@NotNull
private final String name;
private int age;
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
public ClassUser(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.age = age;
}
}

data class类编译后的java代码

Kotlin写法如下:

data class DataClassUser(val name: String, var age: Int)

查看其编译后的java代码如下,会发现比class类编译后的代码多了部分方法,新增了componentscopy方法,重写了equalshashCodetoString方法。

public final class DataClassUser {
@NotNull
private final String name;
private int age;
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
public DataClassUser(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.age = age;
}
// 新增方法
@NotNull
public final String component1() {
return this.name;
}
// 新增方法
public final int component2() {
return this.age;
}
// 新增方法
@NotNull
public final DataClassUser copy(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
return new DataClassUser(name, age);
}
// 新增方法
// $FF: synthetic method
public static DataClassUser copy$default(DataClassUser var0, String var1, int var2, int var3, Object var4) {
if ((var3 & 1) != 0) {
var1 = var0.name;
}
if ((var3 & 2) != 0) {
var2 = var0.age;
}
return var0.copy(var1, var2);
}
// 重写该方法
@NotNull
public String toString() {
return "DataClassUser(name=" + this.name + ", age=" + this.age + ")";
}
// 重写该方法
public int hashCode() {
String var10000 = this.name;
return (var10000 != null ? var10000.hashCode() : 0) * 31 + Integer.hashCode(this.age);
}
// 重写该方法
public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (var1 instanceof DataClassUser) {
DataClassUser var2 = (DataClassUser)var1;
if (Intrinsics.areEqual(this.name, var2.name) && this.age == var2.age) {
return true;
}
}
return false;
} else {
return true;
}
}
}

总结

知道data class干了啥

  • 重写toString方法。
  • 新增componentN方法(component1()、component2()、...、componentN()),其对应属性的声明顺序(常用于解构声明)。
  • 新增copy方法,可以用来修改部分属性,但是保持其他不变。
    特别提下copy方法,可能有些同学疑问很少见到这个方法使用场景,慢慢地等你用上了MVI框架就知道State必须使用 Kotlin data class,copy方法的应用自然少不了。
  • 重写equalshasCode方法,equals()方法不再单一比较对象引用,而是先判断两个是否是同一个对象,如果不是则进行类型判断,是相同类型则逐个比较属性的值。  

使用data class需要注意啥

  • 主构造函数必须要至少有一个参数。
  • 主构造函数中的所有参数必须被标记为val或者var。
  • 数据类不能有以下修饰符:abstract、inner、open、sealed。  


作者:聆听Tech
链接:https://juejin.cn/post/7156233100199100447
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册