kotlin之伴生对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package demo
class Person() {
private var gender: Boolean = true
constructor(name: String, gender: Boolean) : this() {
println("constructor")
}
companion object {
val instance by lazy {
Person("yzq",false)
}
init {
println("companion init 1")
}
init {
println("companion init 2")
}
}
init {
println("Person init 2,gender:${gender}")
}
init {
println("Person init 1")
}
}
/** 输出结果
*companion init 1
*companion init 2
*Person init 2,gender:true
*Person init 1
*constructor
*/

可以看到伴生对象里面的init先执行了。我们看下反编译成Java长什么样,通过java文件我们可以很清楚的就知道为什么输出效果是上面那样的了。可以看到在对象里面,init代码块会被编译到构造函数里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public final class Person {
private boolean gender;
private static final Lazy instance$delegate;
public static final Person.Companion Companion = new Person.Companion((DefaultConstructorMarker)null);
public Person() {
this.gender = true;
String var1 = "Person init 2,gender:" + this.gender;
boolean var2 = false;
System.out.println(var1);
var1 = "Person init 1";
var2 = false;
System.out.println(var1);
}

public Person(@NotNull String name, boolean gender) {
Intrinsics.checkNotNullParameter(name, "name");
this();
String var3 = "constructor";
boolean var4 = false;
System.out.println(var3);
}

static {
instance$delegate = LazyKt.lazy((Function0)null.INSTANCE);
String var0 = "companion init 1";
boolean var1 = false;
System.out.println(var0);
var0 = "companion init 2";
var1 = false;
System.out.println(var0);
}

public static final class Companion {
@NotNull
public final Person getInstance() {
Lazy var1 = Person.instance$delegate;
Person.Companion var2 = Person.Companion;
Object var3 = null;
boolean var4 = false;
return (Person)var1.getValue();
}

private Companion() {
}

public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}

现在换种写法,instance不用懒加载的方式创建,而是直接new。

1
2
// 就这么一点改变
val instance = Person("yzq",false)

反编译成java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public final class Person {
private boolean gender;
@NotNull
private static final Person instance = new Person("yzq", false);
@NotNull
public static final Person.Companion Companion = new Person.Companion((DefaultConstructorMarker)null);
public Person() {
this.gender = true;
String var1 = "Person init 2,gender:" + this.gender;
boolean var2 = false;
System.out.println(var1);
var1 = "Person init 1";
var2 = false;
System.out.println(var1);
}

public Person(@NotNull String name, boolean gender) {
Intrinsics.checkNotNullParameter(name, "name");
this();
String var3 = "constructor";
boolean var4 = false;
System.out.println(var3);
}

static {
String var0 = "companion init 1";
boolean var1 = false;
System.out.println(var0);
var0 = "companion init 2";
var1 = false;
System.out.println(var0);
}
public static final class Companion {
@NotNull
public final Person getInstance() {
return Person.instance;
}

private Companion() {
}

// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
/*
*Person init 2,gender:true
*Person init 1
*constructor
*companion init 1
*companion init 2
*/

可以发现执行顺序变了。

我们可以发现在kotlin里面通过companion object修饰的代码块会被生成为静态变量。欧了。

还有一个好的案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class DS {
var namedsa = ""
constructor(name:String){
this.namedsa = name
}
init {
println("我说${namedsa}")
}
companion object{
fun dsa():DS{
var s = DS("污泥")
return s
}
}
}
fun main() {
DS.dsa()
}

他的输出结果是:我说

是不是很奇怪,污泥怎么不见了?现在来编译成java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public final class DS {
private String namedsa;
public static final DS.Companion Companion = new DS.Companion((DefaultConstructorMarker)null);

@NotNull
public final String getNamedsa() {
return this.namedsa;
}

public final void setNamedsa(@NotNull String var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
this.namedsa = var1;
}

public DS(@NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.namedsa = "";
String var2 = "我说" + this.namedsa;
boolean var3 = false;
System.out.println(var2);
this.namedsa = name;
}
public static final class Companion {
@NotNull
public final DS dsa() {
return new DS("污泥");
}

private Companion() {
}

// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}

我们需要注意的知识点是:init的代码会插入到构造函数里面。他是优先于构造函数里面的任何代码。

好了,现在我在换中写法。构造函数的方式换下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class DS(name:String) {
init {
println("我说${name}")
}
companion object{
fun dsa():DS{
var s = DS("污泥")
return s
}
}
}
fun main() {
DS.dsa()
}

在猜猜,输出结果是啥?

结果是:我说污泥。

来看下java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public final class DS {
public static final DS.Companion Companion = new DS.Companion((DefaultConstructorMarker)null);

public DS(@NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
super();
String var2 = "我说" + name;
boolean var3 = false;
System.out.println(var2);
}
public static final class Companion {
@NotNull
public final DS dsa() {
DS s = new DS("污泥");
return s;
}

private Companion() {
}

public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}

可以看到这样写的话,输出的name直接走的是构造函数里面的name,比起之前的写法,少了一个this.namedsa的赋值操作,所以就出现了两个不同的输出情况。