kotlin数据类

布满荆棘的人生 2023-01-10 01:22 182阅读 0赞

数据类

数据类有点类似java中的实体类,kotlin中可以关键字data class来声明数据类,完成类似java中lombok的功能。

  1. data class User(val name: String, val age: Int)

对于数据类,kotlin会为primary constructor的参数(也是属性)自动生成下面的方法:

  • equals()/hashCode()
  • toString()
  • componentN()
  • copy()
    下面反编译上面定义的User类,看看kotlin编译后的数据类长啥样:

    javap com.morris.kotlin.dataclass.User
    Compiled from “DataClass1.kt”
    public final class com.morris.kotlin.dataclass.User {
    public final java.lang.String getName();
    public final int getAge();
    public com.morris.kotlin.dataclass.User(java.lang.String, int);
    public final java.lang.String component1();
    public final int component2();
    public final com.morris.kotlin.dataclass.User copy(java.lang.String, int);
    public static com.morris.kotlin.dataclass.User copy$default(com.morris.kotlin.dataclass.User, java.lang.String, int, int, java.lang.Object);
    public java.lang.String toString();
    public int hashCode();
    public boolean equals(java.lang.Object);
    }

数据类声明的要点:

  • primary constructor至少有一个参数
  • 所有primary constructor都需要声明为val or var,使其成为属性
  • 数据类不能是abstract, open, sealed or inner

数据类继承的要点:

  • 如果数据类中显示的定义了equals(), hashCode() or toString(),或者在数据类的父类将这些方法声明为final,那么这些方法就不会再生成,转而使用已有的。
  • 如果父类拥有componentN()方法并且是open的以及兼容的返回类型,那么编译器就会生成对应的componentN()方法并重写父类的方法,如果父类中的方法是不兼容的返回类型或者是final,那么编译器就会报错。
  • 在数据类中显示的提供componentN() and copy()是不被允许的。

当数据类中的属性都有默认值时,kotlin会为数据类提供无参的构造方法:

  1. data class User2(val name: String = "", val age: Int = 0)

生成的数据类反编译后的内容如下:

  1. > javap com.morris.kotlin.dataclass.User2
  2. Compiled from "DataClass1.kt"
  3. public final class com.morris.kotlin.dataclass.User2 {
  4. public final java.lang.String getName();
  5. public final int getAge();
  6. public com.morris.kotlin.dataclass.User2(java.lang.String, int);
  7. public com.morris.kotlin.dataclass.User2(java.lang.String, int, int, kotlin.jvm.internal.DefaultConstructorMarker);
  8. public com.morris.kotlin.dataclass.User2();
  9. public final java.lang.String component1();
  10. public final int component2();
  11. public final com.morris.kotlin.dataclass.User2 copy(java.lang.String, int);
  12. public static com.morris.kotlin.dataclass.User2 copy$default(com.morris.kotlin.dataclass.User2, java.lang.String, int, int, java.lang.Object);
  13. public java.lang.String toString();
  14. public int hashCode();
  15. public boolean equals(java.lang.Object);
  16. }

声明在类中的属性

只有声明在构造方法中的属性才会在toString(), equals(), hashCode(), and copy()方法有所体现,而直接声明在类中的属性不会。

  1. data class Person(val name: String) {
  2. var age: Int = 0
  3. }

生成的数据类反编译后的内容如下:

  1. > javap com.morris.kotlin.dataclass.Person
  2. Compiled from "DataClass1.kt"
  3. public final class com.morris.kotlin.dataclass.Person {
  4. public final int getAge();
  5. public final void setAge(int);
  6. public final java.lang.String getName();
  7. public com.morris.kotlin.dataclass.Person(java.lang.String);
  8. public final java.lang.String component1();
  9. public final com.morris.kotlin.dataclass.Person copy(java.lang.String);
  10. public static com.morris.kotlin.dataclass.Person copy$default(com.morris.kotlin.dataclass.Person, java.lang.String, int, java.lang.Object);
  11. public java.lang.String toString();
  12. public int hashCode();
  13. public boolean equals(java.lang.Object);
  14. }

也就意味着下面代码中的两个对象是相等的:

  1. fun main() {
  2. val person1 = Person("John")
  3. val person2 = Person("John")
  4. person1.age = 10
  5. person2.age = 20
  6. assert(person1 == person2) // true
  7. }

拷贝

数据类生成的copy()方法用来拷贝对象的属性,是一个浅拷贝。

User类的copy()方法声明如下:

  1. fun copy(name: String = this.name, age: Int = this.age) = User(name, age)

copy()方法的使用:

  1. val jack = User(name = "Jack", age = 1)
  2. val olderJack = jack.copy(age = 2)
  3. println(olderJack) // User(name=Jack, age=2)

注意:copy()方法是一个浅拷贝,下面的例子证明了这一点:

  1. data class User3(var name : String)
  2. data class Address(var user: User3, var city: String)
  3. fun main() {
  4. var userJack = User3(name="Jack")
  5. var address = Address(user = userJack, city = "London")
  6. var addressCopy = address.copy()
  7. addressCopy.city = "New York"
  8. addressCopy.user.name = "John" // Propagates to `address.user` because they both point to userJack.
  9. println("address.city is ${ address.city}") // Prints "London"
  10. println("address.user.name is ${ address.user.name}") // Prints "John"
  11. }

解构声明

在主构造方法中有多少个参数,就会有多少个componentN()方法,这些方法的返回值就是对应的属性,是用来实现解构声明的。

  1. val jane = User("Jane", 35)
  2. val (name, age) = jane
  3. println("$name, $age years of age") // prints "Jane, 35 years of age"

发表评论

表情:
评论列表 (有 0 条评论,182人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Kotlin密封

    Kotlin密封类 密封类被用于展示受限的类层次结构。当一个值可以有一个来自一个有限集合中的类型,但不能有任何其他类型。在某种程度上来说,它们是枚举类的扩展,一个枚举类型的