今天来学习一下全新关于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要简洁好使得多,可以多多体会。

Kotlin数据类深度解析与底层剖析的更多相关文章

  1. Java 8 Optional 类深度解析

    Java 8 Optional 类深度解析 身为一名Java程序员,大家可能都有这样的经历:调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法.我们首先要判断这个返回值是否为null,只 ...

  2. 深度解析HashMap底层实现架构

    摘要:分析Map接口的详细使用以及HashMap的底层是如何实现的? 本文分享自华为云社区<[图文并茂]深度解析HashMap高频面试及底层实现结构![奔跑吧!JAVA]>,原文作者:灰小 ...

  3. Java基础常用类深度解析(包含常见排序算法)

    目录 一.工具类 1.1.工具类的设计 1.1.1.公共静态方法 1.2.单例模式 二.包装类 2.1.基本类型的包装类 2.1.1.Integer 2.1.1.1.Integer >> ...

  4. Kotlin 数据类与密封类

    数据类 Kotlin 可以创建一个只包含数据的类,关键字为 data: data class User(val name: String, val age: Int) 编译器会自动的从主构造函数中根据 ...

  5. 语言小知识-Java ArrayList类 深度解析

    · 问题 1:ArrayList 的 size 和 capacity 怎么理解? 如果把 ArrayList 看作一个杯子的话,capacity 就是杯子的容积,也就是代表杯子能装多少东西,而 siz ...

  6. Java 8 Optional类深度解析

    身为一名Java程序员,大家可能都有这样的经历:调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法.我们首先要判断这个返回值是否为null,只有在非空的前提下才能将其作为其他方法的参数. ...

  7. Java 8 Optional类深度解析(转)

    经常会遇到这样的问题,调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法.我们首先要判断这个返回值是否为null,只有在非空的前提下才能将其作为其他方法的参数. 新版本的Java,比如J ...

  8. Java 8 Optional类深度解析(转载)

    身为一名Java程序员,大家可能都有这样的经历:调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法.我们首先要判断这个返回值是否为null,只有在非空的前提下才能将其作为其他方法的参数. ...

  9. [转]Java 8 Optional类深度解析(null处理)

    原文链接:http://www.importnew.com/6675.html 本文由 ImportNew - 高俊阳 翻译自 javacodegeeks.欢迎加入翻译小组.转载请见文末要求. 身为一 ...

随机推荐

  1. 使用 Consul 作为 Python 微服务的配置中心

    使用 Consul 作为 Python 微服务的配置中心 Consul 作为数据中心,提供了 k/v 存储的功能,我们可以利用这个功能为 Python 微服务提供配置中心. Consul 提供了 HT ...

  2. 修改服务器时间以后 cookie无法保存

    修改了服务器时间以后发现,cookie无法保存.通过查看异步请求的响应头信息  Date是之前修改的信息,并不是当前的时间. header('Date: Mon, 17 Mar 2015 05:34: ...

  3. [资料]ObjectARX 2020参考指南翻译中文版

    chm使用Google Chrome浏览器翻译,有些翻译不是很理想,因为2万5千多个html文件, 修正难度太大,所以只处理了一部分. 非常感谢 gzxl 辛苦肉眼修正一些翻译问题. 欢迎进入QQ群: ...

  4. Javaspring+mybit+maven中实现定时任务

    背景:在Javaspring中,定时的启动某一个任务时,使用@Scheduled来实现 Javaspring工程创建好之后,直接创建下面的class文件即可.具体的用法可参照 https://www. ...

  5. 第4/7Beta冲刺

    1.团队成员 成员姓名 成员学号 秦裕航 201731062432(组长) 刘东 201731062227 张旭 201731062129 王伟 201731062214 2.SCRU部分 2.1各成 ...

  6. js继承的几种方法理解和代码演示

    1.属性继承 :call .apply:不建议使用浪费内存. function Person(name,age,sex){ this.name = name; this.age = age; this ...

  7. 自定义 Word 默认的 Normal.dotm 模板、更改 Word 默认字体、更改 Word 默认样式(16)

    1. 引言 以Office 2016为例. 有没有遇见这样的问题: 每次新建一个 Word 空白文档打开后字体默认是等线,段落默认是单倍行距,默认标题也不是自己想要的样式,等一系列问题.每次打开都要调 ...

  8. c# 用XmlWriter写xml序列化

    using System.Text; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; using ...

  9. Linux组管理、用户管理、查看用户信息、usermod、which、切换用户、修改文件具体权限

    组管理 提示:创建组/删除组的终端命令都需要通过sudo执行 序号 命令 作用 01 groupadd组名 添加组 02 groupdel组名 删除组 03 cat/etc/group 确认组信息 0 ...

  10. maven 打包错误 Cannot access central in offline mode

    出错的场景是这样的,先是在一台联网的linux机器上下载项目需要的包,linux机器上jdk为1.8 springboot1.5.然后将项目代码和.m2目录拷贝到一台windows机器上,window ...