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


    今天来学习一下全新关于Kotlin的概念---数据类【data class】,也是非常有用的东东,下面先来对其进行理论化的了解:

    数据类其实跟java的实体类(model)很类似,像Java定义一个Person类,里面有各种属性然后再生成它的get和set方法,当然可以借助于IDE来生成,但是其实java的这种做法是挺冗长啰嗦的,当然其实有现成的解决方案来解决这个冗长了,比如用lombok库,利用注解就可以动态生成get和set,接下来咱们来看一下kotlin数据类是怎么使用的:

    这样数据库就定义成了,也没任何额外的get和set方法,是不是特别的简洁,好,下面来使用一下:

    如果将data关键字去掉,其输出就会是对象的toString()了,如下:

    数据类的定义是不是很简单,其实数据类是有一些要求的,下面来瞅下:

    1、主构造方法,至少要有一个参数。

    2、所有的主构造方法参数都需要被标记为var或val。

    3、数据类不能是抽象的、open的、sealed【密封类,还未学到】的以及inner的。

    另外对于数据类,编译器会自动的生成如下内容:

    1、equals/hashcode对。

    2、toString()方法,形式为我们看到的:

    3、针对属性的componentN方法,并且是按照属性的声明顺序来生成的。

    啥意思,也就是针对咱们的这个类会生成component1()、component2()、component3(),而这三个方法的返回值是按照我们定义的name、age、address。

    好,下面来反编译一下,来看一下数据类的底层细节:

    xiongweideMacBook-Pro:kotlin_lecture xiongwei$ javap -c com/kotlin/test2/Person.class
    Compiled from "HelloKotlin1.kt"
    public final class com.kotlin.test2.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.kotlin.test2.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.kotlin.test2.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/kotlin/test2/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.kotlin.test2.Person copy$default(com.kotlin.test2.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/kotlin/test2/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/kotlin/test2/Person
           9: ifeq          66
          12: aload_1
          13: checkcast     #2                  // class com/kotlin/test2/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  #91                 // 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  #91                 // 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
    }
    xiongweideMacBook-Pro:kotlin_lecture xiongwei$ 

    其中是不是可以看到自动为其生成属性的set和get方法了,除了name之外,因为它声明的是一个常量:

    接着生成了两个参数的构造方法:

    再接着就看到我们第三点说的componentN了,如下:

    再往下看:

    还帮我们生成了toString()方法:

    最后就是hashCode()和equals():

    下面继续来对数据类成员的继承要点进行说明:

    1、如果数据类中显示定义了equals,hashCode或者是toString方法,或者是在数据类的父类中将这些方法声明为final,那么这些方法就不会再生成,转而使用已有的。

    2、如果父类拥有componentN方法,并且是open的以及返回兼容的类型,那么编译器就会在数据类中生成相应的componentN方法,并且重写父类的这些方法;如果父类中的这些方法由于不兼容的签名或者被定义为final的,那么编译器就会报错。

    3、在数据类中显示提供componentN方法以及copy方法实现是不允许的。下面来试一下:

    其错误提示为:

    接下来来对生成的copy方法进行一个说明,它的重用主要是根据已有的属性来创建一个新的对象,比如:

    如果咱们这样写:

    接下来想一个问题:既然Kotlin编译主动给属性生成了set和get方法了,那。。是不是可以直接像Java那样来操作属性呢,下面瞅下:

    不通这样写,只能:

    解构声明:

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

    那,如果只写两个变量呢?

    另外,我们通过反编译数据类可以发现,貌似生成的是带参的构造方法,如下:

    那,如何声明不带参的构造方法呢,其实在JVM平台上,如果生成的类需要拥有无参构造方法,那么就需要为所有属性指定默认值,如下:

    再次反编译论证一下:

    xiongweideMacBook-Pro:kotlin_lecture xiongwei$ javap -c com/kotlin/test2/Person2.class
    Compiled from "HelloKotlin1.kt"
    public final class com.kotlin.test2.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.kotlin.test2.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.kotlin.test2.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 henan
          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.kotlin.test2.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.kotlin.test2.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/kotlin/test2/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.kotlin.test2.Person2 copy$default(com.kotlin.test2.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/kotlin/test2/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/kotlin/test2/Person2
           9: ifeq          66
          12: aload_1
          13: checkcast     #2                  // class com/kotlin/test2/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  #98                 // 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  #98                 // 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
    }

    果真如此,跟Java不同的是,如果Java中的属性没有赋值会有默认值,而在Kotlin中默认是不会自动给默认值给属性进行赋值的,其实这样做更加的安全,这样既便我们在使用Kotlin的无参构造方法时,它里面的属性也能保证都有初始值。

    关于数据类在实际中使用是非常多的,可见也比Java要简洁好使得多,可以多多体会。

  • 相关阅读:
    【BZOJ 3238】 3238: [Ahoi2013]差异(SAM)
    【BZOJ 4180】 4180: 字符串计数 (SAM+二分+矩阵乘法)
    【BZOJ 3676】 3676: [Apio2014]回文串 (SAM+Manacher+倍增)
    【BZOJ 3998】 3998: [TJOI2015]弦论 (SAM )
    【BZOJ 2946】 2946: [Poi2000]公共串 (SAM)
    【BZOJ 1398】 1398: Vijos1382寻找主人 Necklace (最小表示法)
    【BZOJ 4031】 4031: [HEOI2015]小Z的房间 (Matrix-Tree Theorem)
    【BZOJ 3534】 3534: [Sdoi2014]重建 (Matrix-Tree Theorem)
    【BZOJ 3659】 3659: Which Dreamed It (Matrix-Tree&BEST theorem )
    【BZOJ 4596】 4596: [Shoi2016]黑暗前的幻想乡 (容斥原理+矩阵树定理)
  • 原文地址:https://www.cnblogs.com/webor2006/p/11234941.html
Copyright © 2020-2023  润新知