kotlin通过data class创建数据类。
package com.example.kotlindemo.classsdemo
data class PayDataBird(var weight: Double, var age: Int, var color: String)
Kotlin转java
public final class PayDataBird {
private double weight;
private int age;
@NotNull
private String color;
public final double getWeight() {
return this.weight;
}
public final void setWeight(double var1) {
this.weight = var1;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
@NotNull
public final String getColor() {
return this.color;
}
public final void setColor(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "");
this.color = var1;
}
public PayDataBird(double weight, int age, @NotNull String color) {
Intrinsics.checkParameterIsNotNull(color, "color");
super();
this.weight = weight;
this.age = age;
this.color = color;
}
public final double component1() {
return this.weight;
}
public final int component2() {
return this.age;
}
@NotNull
public final String component3() {
return this.color;
}
@NotNull
public final PayDataBird copy(double weight, int age, @NotNull String color) {
Intrinsics.checkParameterIsNotNull(color, "color");
return new PayDataBird(weight, age, color);
}
// $FF: synthetic method
public static PayDataBird copy$default(PayDataBird var0, double var1, int var3, String var4, int var5, Object var6) {
if ((var5 & 1) != 0) {
var1 = var0.weight;
}
if ((var5 & 2) != 0) {
var3 = var0.age;
}
if ((var5 & 4) != 0) {
var4 = var0.color;
}
return var0.copy(var1, var3, var4);
}
@NotNull
public String toString() {
return "PayDataBird(weight=" + this.weight + ", age=" + this.age + ", color=" + this.color + ")";
}
public int hashCode() {
long var10000 = Double.doubleToLongBits(this.weight);
int var1 = ((int)(var10000 ^ var10000 >>> 32) * 31 + this.age) * 31;
String var10001 = this.color;
return var1 + (var10001 != null ? var10001.hashCode() : 0);
}
public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (var1 instanceof PayDataBird) {
PayDataBird var2 = (PayDataBird)var1;
if (Double.compare(this.weight, var2.weight) == 0 && this.age == var2.age && Intrinsics.areEqual(this.color, var2.color)) {
return true;
}
}
return false;
} else {
return true;
}
}
}
转换成Java代码后,发现和一般我们定义的JavaBean代码非常相似,同样有getter/setter方法、equals、hashcode、构造函数等方法,其中equals和hashcode使得一个数据类对象可以像普通类型的实例一样进行判等,也可以像基本数据类型一样用==来判断两个对象相等。
fun main() {
val b1 = PayDataBird(weight = 1000.0, age = 1, color = "blue")
val b2 = PayDataBird(weight = 1000.0, age = 1, color = "blue")
println(b1.equals(b2))
println(b1 == b2)
}
true
true
可以发现两个特别的方法:copy与componentN。
@NotNull
public final PayDataBird copy(double weight, int age, @NotNull String color) {
Intrinsics.checkParameterIsNotNull(color, "color");
return new PayDataBird(weight, age, color);
}
// $FF: synthetic method
public static PayDataBird copy$default(PayDataBird var0, double var1, int var3, String var4, int var5, Object var6) {
if ((var5 & 1) != 0) {
var1 = var0.weight;//var0代表被copy的对象,copy时如果未指定具体属性的值,则使用被copy对象的属性值。
}
if ((var5 & 2) != 0) {
var3 = var0.age;
}
if ((var5 & 4) != 0) {
var4 = var0.color;
}
return var0.copy(var1, var3, var4);
}
copy方法的作用是:从已有的数据类对象中拷贝一个新的数据类对象,当然可以传入相应参数来生成不同的对象。在copy过程中,如果未指定具体属性的值,那么新生成的对象的属性值将使用被copy对象的属性值,也就是浅拷贝。
浅拷贝存在问题
val payDataBirdA = PayDataBird(weight = 1000.0, age = 1, color = "blue")
val payDataBirdB = payDataBirdA
payDataBirdB.color = "red"
println(payDataBirdA.color) // 要注意浅拷贝的影响
red
copy更像一种语法糖,假设类是不可变的,属性不可以修改,则只能通过copy来基于原有对象生成一个新的对象。
data class PayDataBird(val weight: Double, val age: Int, val color: String)
val payDataBirdA = PayDataBird(weight = 1000.0, age = 1, color = "blue")
val payDataBirdB = payDataBirdA.copy(2000.0, 2, "red")
println(payDataBirdA.color)
println(payDataBirdB.color)
blue
red
copy更像是提供了一种简洁的方式帮助我们复制来一个对象,但它是一种浅拷贝的方式。所以在使用copy的时候要注意使用场景,因为数据类的属性可以被修饰为var,所以不能保证修改引用的问题。componentN可以理解为类属性的值,其中N代表属性的顺序。比如component1代表第1个属性的值,component3代表第3个属性的值。
这样设计的作用是什么?
我们知道如何将属性绑定到类上,那么如何将类的属性绑定到相应的变量上呢?
val payDataBird = PayDataBird(weight = 1000.0, age = 1, color = "blue")
//通常方式
val weight = payDataBird.weight
val age = payDataBird.age
val color = payDataBird.color
//kotlin进阶
val (weight, age, color) = payDataBird
观察下面代码
val birdInfo = "20.0,1,blue"
val temps = birdInfo.split(",")
val weight = temps[0].toDouble()
val age = temps[1].toInt()
val color = temps[2]
println("$weight,$age,$color")
20.0,1,blue
上面的代码很繁琐,kotlin提供更加方便的代码
val (weight, age, color) = birdInfo.split(",")
println("$weight,$age,$color")
20.0,1,blue
解构,通过编译器的约定实现解构。
kotlin对于数组的解构有一定的限制,在数组中默认最多允许赋值5个变量,因为若变量过多,效果反而不好,因为容易搞不清哪个值要赋给哪个变量。所以需要合理使用这个特性。
在数据类中,除了可以利用编译器帮你自动生成componentN以外,甚至还可以自己实现对应属性的componentN方法。
data class PayDataBird(val weight: Double, val age: Int, val color: String) {
var sex = 1
operator fun component4(): Int { //注意operate关键字
return this.sex
}
constructor(weight: Double, age: Int, color: String, sex: Int) : this(weight, age, color) {
this.sex = sex
}
}
val payDataBird = PayDataBird(weight = 1000.0, age = 1, color = "blue", sex = 0)
val (weight, age, color, sex) = payDataBird
println("$weight,$age,$color,$sex")
1000.0,1,blue,0
除了数组支持解构外,Kotlin提供了其他常用的数据类,Pair和Triple。Pair是二元数组,可以理解为这个数据类中有两个属性。
Triple是三元数组,对应的是3个属性。
* @param A type of the first value.
* @param B type of the second value.
* @property first First value.
* @property second Second value.
* @constructor Creates a new instance of Pair.
*/
public data class Pair(
public val first: A,
public val second: B
) : Serializable {
......
}
* @param A type of the first value.
* @param B type of the second value.
* @param C type of the third value.
* @property first First value.
* @property second Second value.
* @property third Third value.
*/
public data class Triple(
public val first: A,
public val second: B,
public val third: C
) : Serializable {
可以发现Pair和Triple都是数据类,它们的属性可以是任意类型,我们可以按照属性的顺序来获取对应属性的值。
val payPair = Pair(20.0, 1)
val payTriple = Triple(20.0, 1, "blue")
//利用属性顺序获取
val weightP = payPair.first
val ageP = payPair.second
println("$weightP,$ageP")
val weightT = payTriple.first
val ageT = payTriple.second
val colorT = payTriple.third
println("$weightT,$ageT,$colorT")
20.0,1
20.0,1,blue
通过解构优化
//通过解构解决
val (payWeightP, payAgeP) = Pair(20.0, 1)
val (payWeightT, payAgeT, payColorT) = Triple(20.0, 1, "blue")
println("$payWeightP,$payAgeP")
println("$payWeightT,$payAgeT,$payColorT")
数据类中的解构基于componentN函数,如果自己不声明componentN函数,那么就会默认根据主构造函数来生成具体个数的componentN函数,与从构造函数的参数无关。
数据类的约定和使用
Kotlin中声明一个数据类,必须满足以下几个条件:
1.数据类必须拥有构造方法,该方法必须至少拥有一个参数,一个没有数据的数据类没有任何用处。
2.与普通类的不同,数据类的构造方法的参数强制使用var或者val进行声明。
3.data class之前不能使用abstract、open、sealed或者inner进行修饰。
4.kotlin1.1版本之前数据类只允许实现接口,之后的版本既可以实现接口,也可以继承类。
参考Kotlin核心编程