本节介绍kotlin的data class(数据类)
1、数据类的定义和反编译分析
在Java中数据类中具有的 getter,setter方法,可以通过 IDEA或者eclipse来生成代码,但是有很多的冗余
当然我们也可以使用Java的插件,lombok,仅用几个注释就能解决。
1.1、数据类的定义
但是在kotlin中,我们可以直接使用 data 关键字来定义数据类
注意数据类的前提条件:
- 1、主构造方法至少要有一个参数
- 2、所有的主构造方法参数都需要被标记为 var 或者 val
- 3、数据类不能是抽象、open的、sealed(密封类)、inner的类
数据类的定义示例:
data class Person(val name: String, var age: Int, var address: String)
数据类的使用:
fun main() {
val person = Person("zhangsan", 20, "beijing")
println(person)
}
1.2、数据类的反编译分析
接下来,我们可以使用反编译,对上面生成的数据类进行深度的分析。(关于反编译知识,看一看我之前的博客,点这里 ) ,以下是反编译结果:
Compiled from "DataClass.kt"
public final class com.liang.kotlin.basic.data_class.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.liang.kotlin.basic.data_class.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.liang.kotlin.basic.data_class.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/liang/kotlin/basic/data_class/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.liang.kotlin.basic.data_class.Person copy$default(com.liang.kotlin.basic.data_class.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/liang/kotlin/basic/data_class/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 56
5: aload_1
6: instanceof #2 // class com/liang/kotlin/basic/data_class/Person
9: ifeq 58
12: aload_1
13: checkcast #2 // class com/liang/kotlin/basic/data_class/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 58
31: aload_0
32: getfield #19 // Field age:I
35: aload_2
36: getfield #19 // Field age:I
39: if_icmpne 58
42: aload_0
43: getfield #26 // Field address:Ljava/lang/String;
46: aload_2
47: getfield #26 // Field address:Ljava/lang/String;
50: invokestatic #91 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
53: ifeq 58
56: iconst_1
57: ireturn
58: iconst_0
59: ireturn
}
通过上面的反编译,我们可以清楚的看到:
编译器做增加了一下内容:
1、equals/hasCode
2、toString(),形式为:Person(name=..., age=..., address=...)
3、针对属性的componentN方法,并且是按照属性的声明顺序来生成的
4、copy方法
2、数据类方法说明
通过上面的反编译class,接下来对编译器生成出来的函数进行探索。
2.1、componentN()
componentN的方法主要在于,解构声明:
- 解构:在主构造方法中有多少个参数就会生成多少个 component方法 (component1,component2... )
- 这些方法返回对应字段的值,component方法是用来实现 解构声明的
演示如图所示:
2.2、copy()
copy() : 作用在赋值的时候,很方便,
在Java中,一个对象的类成员如果如果被另一个对象复制,仅仅修改了一个参数,那么需要把这个对象的所有值都赋值给另一个对象,这样是非常麻烦的。
在kotlin中,可以通过copy()方法执行修改某一个参数
注意: 如果不加参数名字,那么默认是第一个,必须明确参数名