美文网首页
11.Kotlin数据类深度解析与底层剖析

11.Kotlin数据类深度解析与底层剖析

作者: leofight | 来源:发表于2017-12-24 18:00 被阅读0次

1.数据类

数据类 -- data class:类似于java中的实体类,与数据库映射的类。如:领域模型、domain、model等。

数据类需要满足如下要求:
①主构造方法至少要有一个参数。
②所有的主构造方法参数都需要被标记为var或者是val。
③数据类不能是抽象的、open、sealed的以及inner。

对于数据类,编译器会自动生成如下内容:
①equals/hashCode对。
②toString()方法,形式为Person(name=...,age=...,address=...)
③针对属性的componentN方法,并且是按照属性的声明顺序来生成的。

  • 示例代码
data class Person(val name: String, var age: Int, var address: String)

fun main(args: Array<String>) {
    var person = Person("zhangsan",20,"beijing")
    println(person)
}
  • 输出结果
Person(name=zhangsan, age=20, address=beijing)
  • 反编译Person
➜  kotlin_lecture javap -c com.leofight.kotlin2.Person
Compiled from "HelloKotlin1.kt"
public final class com.leofight.kotlin2.Person {
  public final java.lang.String getName();
    Code:
       0: aload_0
       1: getfield      #11                 // Field name:Ljava/lang/String;
       4: areturn

  public final int getAge();
    Code:
       0: aload_0
       1: getfield      #19                 // Field age:I
       4: ireturn

  public final void setAge(int);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #19                 // Field age:I
       5: return

  public final java.lang.String getAddress();
    Code:
       0: aload_0
       1: getfield      #26                 // Field address:Ljava/lang/String;
       4: areturn

  public final void setAddress(java.lang.String);
    Code:
       0: aload_1
       1: ldc           #29                 // String <set-?>
       3: invokestatic  #35                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: aload_1
       8: putfield      #26                 // Field address:Ljava/lang/String;
      11: return

  public com.leofight.kotlin2.Person(java.lang.String, int, java.lang.String);
    Code:
       0: aload_1
       1: ldc           #38                 // String name
       3: invokestatic  #35                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_3
       7: ldc           #39                 // String address
       9: invokestatic  #35                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_0
      13: invokespecial #42                 // Method java/lang/Object."<init>":()V
      16: aload_0
      17: aload_1
      18: putfield      #11                 // Field name:Ljava/lang/String;
      21: aload_0
      22: iload_2
      23: putfield      #19                 // Field age:I
      26: aload_0
      27: aload_3
      28: putfield      #26                 // Field address:Ljava/lang/String;
      31: return

  public final java.lang.String component1();
    Code:
       0: aload_0
       1: getfield      #11                 // Field name:Ljava/lang/String;
       4: areturn

  public final int component2();
    Code:
       0: aload_0
       1: getfield      #19                 // Field age:I
       4: ireturn

  public final java.lang.String component3();
    Code:
       0: aload_0
       1: getfield      #26                 // Field address:Ljava/lang/String;
       4: areturn

  public final com.leofight.kotlin2.Person copy(java.lang.String, int, java.lang.String);
    Code:
       0: aload_1
       1: ldc           #38                 // String name
       3: invokestatic  #35                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_3
       7: ldc           #39                 // String address
       9: invokestatic  #35                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
      12: new           #2                  // class com/leofight/kotlin2/Person
      15: dup
      16: aload_1
      17: iload_2
      18: aload_3
      19: invokespecial #49                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
      22: areturn

  public static com.leofight.kotlin2.Person copy$default(com.leofight.kotlin2.Person, java.lang.String, int, java.lang.String, int, java.lang.Object);
    Code:
       0: iload         4
       2: iconst_1
       3: iand
       4: ifeq          12
       7: aload_0
       8: getfield      #11                 // Field name:Ljava/lang/String;
      11: astore_1
      12: iload         4
      14: iconst_2
      15: iand
      16: ifeq          24
      19: aload_0
      20: getfield      #19                 // Field age:I
      23: istore_2
      24: iload         4
      26: iconst_4
      27: iand
      28: ifeq          36
      31: aload_0
      32: getfield      #26                 // Field address:Ljava/lang/String;
      35: astore_3
      36: aload_0
      37: aload_1
      38: iload_2
      39: aload_3
      40: invokevirtual #53                 // Method copy:(Ljava/lang/String;ILjava/lang/String;)Lcom/leofight/kotlin2/Person;
      43: areturn

  public java.lang.String toString();
    Code:
       0: new           #56                 // class java/lang/StringBuilder
       3: dup
       4: invokespecial #57                 // Method java/lang/StringBuilder."<init>":()V
       7: ldc           #59                 // String Person(name=
       9: invokevirtual #63                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: aload_0
      13: getfield      #11                 // Field name:Ljava/lang/String;
      16: invokevirtual #63                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: ldc           #65                 // String , age=
      21: invokevirtual #63                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: aload_0
      25: getfield      #19                 // Field age:I
      28: invokevirtual #68                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      31: ldc           #70                 // String , address=
      33: invokevirtual #63                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      36: aload_0
      37: getfield      #26                 // Field address:Ljava/lang/String;
      40: invokevirtual #63                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      43: ldc           #72                 // String )
      45: invokevirtual #63                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      48: invokevirtual #74                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      51: areturn

  public int hashCode();
    Code:
       0: aload_0
       1: getfield      #11                 // Field name:Ljava/lang/String;
       4: dup
       5: ifnull        14
       8: invokevirtual #77                 // Method java/lang/Object.hashCode:()I
      11: goto          16
      14: pop
      15: iconst_0
      16: bipush        31
      18: imul
      19: aload_0
      20: getfield      #19                 // Field age:I
      23: invokestatic  #82                 // Method java/lang/Integer.hashCode:(I)I
      26: iadd
      27: bipush        31
      29: imul
      30: aload_0
      31: getfield      #26                 // Field address:Ljava/lang/String;
      34: dup
      35: ifnull        44
      38: invokevirtual #77                 // Method java/lang/Object.hashCode:()I
      41: goto          46
      44: pop
      45: iconst_0
      46: iadd
      47: ireturn

  public boolean equals(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: if_acmpeq     64
       5: aload_1
       6: instanceof    #2                  // class com/leofight/kotlin2/Person
       9: ifeq          66
      12: aload_1
      13: checkcast     #2                  // class com/leofight/kotlin2/Person
      16: astore_2
      17: aload_0
      18: getfield      #11                 // Field name:Ljava/lang/String;
      21: aload_2
      22: getfield      #11                 // Field name:Ljava/lang/String;
      25: invokestatic  #90                 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
      28: ifeq          66
      31: aload_0
      32: getfield      #19                 // Field age:I
      35: aload_2
      36: getfield      #19                 // Field age:I
      39: if_icmpne     46
      42: iconst_1
      43: goto          47
      46: iconst_0
      47: ifeq          66
      50: aload_0
      51: getfield      #26                 // Field address:Ljava/lang/String;
      54: aload_2
      55: getfield      #26                 // Field address:Ljava/lang/String;
      58: invokestatic  #90                 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
      61: ifeq          66
      64: iconst_1
      65: ireturn
      66: iconst_0
      67: ireturn
}

关于数据类成员的继承要点:
①如果数据类中显式定义了equals,hashCode或是toString方法,或者是在数据类的父类中将这些方法声明为了final,那么这些方法就不会再生成,转而使用已有的。
②如果父类拥有componentN方法,并且是open的以及返回兼容的类型,那么编译器就会在数据类中生成相应的componentN方法,并且重写父类的这些方法;如果父类中的这些方法由于不兼容的签名或者定义为final的,那么编译器就会报错。
③在数据类中显式提供componentN方法以及copy方法实现是不允许的。

解构声明
在主构造方法中又多少个参数,就会依次生成对应的component1、component2、component3...这些方法返回的就是对应字段的值,componentN方法是用来实现解构声明的。

  • 示例代码
data class Person(val name: String, var age: Int, var address: String){

}

fun main(args: Array<String>) {
    var person = Person("zhangsan",20,"beijing")
    println(person)

    person.age = 30
    println(person.age)

    var person2 = person.copy(address = "hangzhou")
    println(person2)

    //解构
    var (name,age,address)  = person
    println("$name,$age,$address")
}
  • 运行结果
Person(name=zhangsan, age=20, address=beijing)
30
Person(name=zhangsan, age=30, address=hangzhou)
zhangsan,30,beijing

在JVM平台上,如果生成的类需要拥有无参构造方法,那么就需要为所有属性指定默认值。

  • 示例代码
data class Person2(val name:String = "",var age: Int = 20, var address: String = "tianjing")
  • 反编译Person2
➜  kotlin_lecture javap -c com.leofight.kotlin2.Person2
Compiled from "HelloKotlin1.kt"
public final class com.leofight.kotlin2.Person2 {
  public final java.lang.String getName();
    Code:
       0: aload_0
       1: getfield      #11                 // Field name:Ljava/lang/String;
       4: areturn

  public final int getAge();
    Code:
       0: aload_0
       1: getfield      #19                 // Field age:I
       4: ireturn

  public final void setAge(int);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #19                 // Field age:I
       5: return

  public final java.lang.String getAddress();
    Code:
       0: aload_0
       1: getfield      #26                 // Field address:Ljava/lang/String;
       4: areturn

  public final void setAddress(java.lang.String);
    Code:
       0: aload_1
       1: ldc           #29                 // String <set-?>
       3: invokestatic  #35                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: aload_1
       8: putfield      #26                 // Field address:Ljava/lang/String;
      11: return

  public com.leofight.kotlin2.Person2(java.lang.String, int, java.lang.String);
    Code:
       0: aload_1
       1: ldc           #38                 // String name
       3: invokestatic  #35                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_3
       7: ldc           #39                 // String address
       9: invokestatic  #35                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_0
      13: invokespecial #42                 // Method java/lang/Object."<init>":()V
      16: aload_0
      17: aload_1
      18: putfield      #11                 // Field name:Ljava/lang/String;
      21: aload_0
      22: iload_2
      23: putfield      #19                 // Field age:I
      26: aload_0
      27: aload_3
      28: putfield      #26                 // Field address:Ljava/lang/String;
      31: return

  public com.leofight.kotlin2.Person2(java.lang.String, int, java.lang.String, int, kotlin.jvm.internal.DefaultConstructorMarker);
    Code:
       0: iload         4
       2: iconst_1
       3: iand
       4: ifeq          10
       7: ldc           #45                 // String
       9: astore_1
      10: iload         4
      12: iconst_2
      13: iand
      14: ifeq          20
      17: bipush        20
      19: istore_2
      20: iload         4
      22: iconst_4
      23: iand
      24: ifeq          30
      27: ldc           #47                 // String tianjing
      29: astore_3
      30: aload_0
      31: aload_1
      32: iload_2
      33: aload_3
      34: invokespecial #49                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
      37: return

  public com.leofight.kotlin2.Person2();
    Code:
       0: aload_0
       1: aconst_null
       2: iconst_0
       3: aconst_null
       4: bipush        7
       6: aconst_null
       7: invokespecial #51                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
      10: return

  public final java.lang.String component1();
    Code:
       0: aload_0
       1: getfield      #11                 // Field name:Ljava/lang/String;
       4: areturn

  public final int component2();
    Code:
       0: aload_0
       1: getfield      #19                 // Field age:I
       4: ireturn

  public final java.lang.String component3();
    Code:
       0: aload_0
       1: getfield      #26                 // Field address:Ljava/lang/String;
       4: areturn

  public final com.leofight.kotlin2.Person2 copy(java.lang.String, int, java.lang.String);
    Code:
       0: aload_1
       1: ldc           #38                 // String name
       3: invokestatic  #35                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_3
       7: ldc           #39                 // String address
       9: invokestatic  #35                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
      12: new           #2                  // class com/leofight/kotlin2/Person2
      15: dup
      16: aload_1
      17: iload_2
      18: aload_3
      19: invokespecial #49                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
      22: areturn

  public static com.leofight.kotlin2.Person2 copy$default(com.leofight.kotlin2.Person2, java.lang.String, int, java.lang.String, int, java.lang.Object);
    Code:
       0: iload         4
       2: iconst_1
       3: iand
       4: ifeq          12
       7: aload_0
       8: getfield      #11                 // Field name:Ljava/lang/String;
      11: astore_1
      12: iload         4
      14: iconst_2
      15: iand
      16: ifeq          24
      19: aload_0
      20: getfield      #19                 // Field age:I
      23: istore_2
      24: iload         4
      26: iconst_4
      27: iand
      28: ifeq          36
      31: aload_0
      32: getfield      #26                 // Field address:Ljava/lang/String;
      35: astore_3
      36: aload_0
      37: aload_1
      38: iload_2
      39: aload_3
      40: invokevirtual #60                 // Method copy:(Ljava/lang/String;ILjava/lang/String;)Lcom/leofight/kotlin2/Person2;
      43: areturn

  public java.lang.String toString();
    Code:
       0: new           #63                 // class java/lang/StringBuilder
       3: dup
       4: invokespecial #64                 // Method java/lang/StringBuilder."<init>":()V
       7: ldc           #66                 // String Person2(name=
       9: invokevirtual #70                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: aload_0
      13: getfield      #11                 // Field name:Ljava/lang/String;
      16: invokevirtual #70                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: ldc           #72                 // String , age=
      21: invokevirtual #70                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: aload_0
      25: getfield      #19                 // Field age:I
      28: invokevirtual #75                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      31: ldc           #77                 // String , address=
      33: invokevirtual #70                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      36: aload_0
      37: getfield      #26                 // Field address:Ljava/lang/String;
      40: invokevirtual #70                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      43: ldc           #79                 // String )
      45: invokevirtual #70                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      48: invokevirtual #81                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      51: areturn

  public int hashCode();
    Code:
       0: aload_0
       1: getfield      #11                 // Field name:Ljava/lang/String;
       4: dup
       5: ifnull        14
       8: invokevirtual #84                 // Method java/lang/Object.hashCode:()I
      11: goto          16
      14: pop
      15: iconst_0
      16: bipush        31
      18: imul
      19: aload_0
      20: getfield      #19                 // Field age:I
      23: invokestatic  #89                 // Method java/lang/Integer.hashCode:(I)I
      26: iadd
      27: bipush        31
      29: imul
      30: aload_0
      31: getfield      #26                 // Field address:Ljava/lang/String;
      34: dup
      35: ifnull        44
      38: invokevirtual #84                 // Method java/lang/Object.hashCode:()I
      41: goto          46
      44: pop
      45: iconst_0
      46: iadd
      47: ireturn

  public boolean equals(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: if_acmpeq     64
       5: aload_1
       6: instanceof    #2                  // class com/leofight/kotlin2/Person2
       9: ifeq          66
      12: aload_1
      13: checkcast     #2                  // class com/leofight/kotlin2/Person2
      16: astore_2
      17: aload_0
      18: getfield      #11                 // Field name:Ljava/lang/String;
      21: aload_2
      22: getfield      #11                 // Field name:Ljava/lang/String;
      25: invokestatic  #97                 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
      28: ifeq          66
      31: aload_0
      32: getfield      #19                 // Field age:I
      35: aload_2
      36: getfield      #19                 // Field age:I
      39: if_icmpne     46
      42: iconst_1
      43: goto          47
      46: iconst_0
      47: ifeq          66
      50: aload_0
      51: getfield      #26                 // Field address:Ljava/lang/String;
      54: aload_2
      55: getfield      #26                 // Field address:Ljava/lang/String;
      58: invokestatic  #97                 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
      61: ifeq          66
      64: iconst_1
      65: ireturn
      66: iconst_0
      67: ireturn
}

相关文章

网友评论

      本文标题:11.Kotlin数据类深度解析与底层剖析

      本文链接:https://www.haomeiwen.com/subject/wfyhgxtx.html