数据类

数据类(Data Class) 顾名思义,用于存放数据的类Kotlin 规划者深知开发者需求的数据类应该具有哪些能力,乃至直接为其规划了一个关键字 data,所以通过 data class 界说的数据类从规划层面就支撑自动生成一些例如 equals、hashCode 之类的办法,方便了开发者的同时其中也有一些值得注意的细节,我们把它反编译成 Java 代码便可一目了然

data 关键字带来的改变

如下是两个数据类,仅有差异就是一个有 data 关键字,一个没有

class Person(
  val name: String,
  var age: String,
  val isAdult: Boolean,
)
data class DataPerson(
  val name: String,
  var age: String,
  val isAdult: Boolean,
)

反编译成 Java 代码的差异比照

public final class Person {
 private final String name;
 private int age;
 private final boolean isAdult;
 public final String getName()
 public final int getAge()
 public final void setAge(int age)
 public final boolean isAdult()
 public Person(@NotNull String name, int age, boolean isAdult) {
   this.name = name;
   this.age = age;
   this.isAdult = isAdult;
 }
}
public final class DataPerson {
 // ... 省掉相同部分
   public final <Type> componentN()
   public final DataPerson copy(@NotNull String name, int age, boolean isAdult)
   public String toString()
   public int hashCode()
   public boolean equals(@Nullable Object var1)
}

能够看到添加了 data 关键字之后编译器为我们自动生成了一些有用的办法

  • componentN() 用于解构声明
  • copy()
  • toString()
  • hashCode()
  • equals()

派生特点

创立数据类时,有些特点能够通过其它特点计算得到,这时候要依据情况挑选是否使用 getter

class Person(
  val name: String,
  var age: String,
) {
    val isAdult = age > 17      // ① ❌
    val adult get() = age > 17  // ② ✅
}

代码 ① 特点会被声明为 final 类型并初始化在结构办法中,代码 ② 特点则会直接被编译成 get 办法,如下是对应反编译的 Java 代码

public final class Person {
   private final boolean isAdult;
   @NotNull
   private final String name;
   private int age;
   public final boolean isAdult() {
      return this.isAdult;
   }
   public final boolean getAdult() {
      return this.age > 17;
   }
   @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 Person(@NotNull String name, int age) {
      Intrinsics.checkNotNullParameter(name, "name");
      super();
      this.name = name;
      this.age = age;
      this.isAdult = this.age > 17;
   }
}

可见 Person 被创立之后,假如 age 被改变,isAdult 的成果不会受到影响,由于它在对象初始化的时候就计算出固定成果了。adult 的成果会对应发生预期的改变

fun main() {
    val person = Person(name = "", age = 17)
    person.age = 18
    println("isAdult ${person.isAdult}")  // false
    println("adult ${person.adult}")      // true
}

所以,关于那些依据可变特点的改变而派生出来的特点,需求自界说 getter 而不是直接赋予表达式。依据不可变特点派生出来的特点则不需求 getter

class Rectangle(val width: Int, val height: Int) {
    val area = this.width * this.height  // ✅
}