kotlin面向对象总结-数据类

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, "<set-?>");
      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<out A, out B>(
    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<out A, out B, out C>(
    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核心编程 

发布了179 篇原创文章 · 获赞 175 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/zhangying1994/article/details/104212729