In Java, we will create some classes dedicated to holding data, such as various classes ending with Bean and Model as suffixes.
The member variables of these classes are usually various types of data, and the member functions are setters and getters. Or lazy students directly set the visibility of member variables to public, saving setters and getters.
Although we can omit the template code of setter & getter, toString(), equals(), hashCode() and copy() methods still need to be implemented manually.
Kotlin has specially set up data classes to simplify the template code and make the code more concise.
Let's take the class purchase as an example:
data class Puppy( val name: String, val age: Int, val cuteness: Int, ) { var breed: String? = null } // usage method val tofu = Puppy(name = "Tofu", age = 1, cuteness = Int.MAX_VALUE) val taco = Puppy(name = "Taco", age = 2) fun play() { val anotherTaco = taco.copy(name = "Tofu2") // The remaining member variables use existing values }
Let's look at the decompiled Java code:
public final class Puppy { @Nullable private String breed; @NotNull private final String name; private final int age; @NotNull private final String cuteness; @Nullable public final String getBreed() { return this.breed; } public final void setBreed(@Nullable String var1) { this.breed = var1; } @NotNull public final String getName() { return this.name; } public final int getAge() { return this.age; } @NotNull public final String getCuteness() { return this.cuteness; } public Puppy(@NotNull String name, int age, @NotNull String cuteness) { Intrinsics.checkParameterIsNotNull(name, "name"); Intrinsics.checkParameterIsNotNull(cuteness, "cuteness"); super(); this.name = name; this.age = age; this.cuteness = cuteness; } @NotNull public final String component1() { return this.name; } public final int component2() { return this.age; } @NotNull public final String component3() { return this.cuteness; } @NotNull public final Puppy copy(@NotNull String name, int age, @NotNull String cuteness) { Intrinsics.checkParameterIsNotNull(name, "name"); Intrinsics.checkParameterIsNotNull(cuteness, "cuteness"); return new Puppy(name, age, cuteness); } // $FF: synthetic method public static Puppy copy$default(Puppy var0, String var1, int var2, String var3, int var4, Object var5) { if ((var4 & 1) != 0) { var1 = var0.name; } if ((var4 & 2) != 0) { var2 = var0.age; } if ((var4 & 4) != 0) { var3 = var0.cuteness; } return var0.copy(var1, var2, var3); } @NotNull public String toString() { return "Puppy(name=" + this.name + ", age=" + this.age + ", cuteness=" + this.cuteness + ")"; } public int hashCode() { String var10000 = this.name; int var1 = ((var10000 != null ? var10000.hashCode() : 0) * 31 + this.age) * 31; String var10001 = this.cuteness; return var1 + (var10001 != null ? var10001.hashCode() : 0); } public boolean equals(@Nullable Object var1) { if (this != var1) { if (var1 instanceof Puppy) { Puppy var2 = (Puppy)var1; if (Intrinsics.areEqual(this.name, var2.name) && this.age == var2.age && Intrinsics.areEqual(this.cuteness, var2.cuteness)) { return true; } } return false; } else { return true; } } }
As you can see, data class is for each attribute:
- Getter & setter is automatically generated
- Automatically generate equals () & hashcode () & copy () & toString () methods using the attributes in the main constructor
It's not too convenient! Finally get rid of annoying template code!
In the decompiled Java code, several functions are conspicuous:
@NotNull public final String component1() { return this.name; } public final int component2() { return this.age; } @NotNull public final String component3() { return this.cuteness; }
What is a component? What is the function? In what scenario?
At the same time, we need to pay attention to:
- The data class cannot be abstract, open, sealed, or inner, otherwise an error will be reported
- Data classes can inherit other classes or interfaces or abstract classes, but they cannot inherit other data classes
- The main constructor of a data class can only be member variables of type val/var. only the member variables in the main constructor can be used to generate equals() & hashcode() & copy() & tostring() methods, and the member variables in the class will not
- It is recommended to use the val type for the member variables in the main construction method of the data class, because when the instance of the data class is used as the key of containers such as HashMap, the change of the var type variable will cause us to get the wrong value
Koltin has two built-in data classes, Pair and Triple, which are used to carry two and three member variables respectively.