一:class className([car/val] property: Type…)
这种方式和上面一种方式多加了一组括号,代表构造函数,我们把这样的构造函数称之为primary constructor。这种方式声明一个类的主要做了一下几件事:
- 会生成一个构造方法,参数就是括号里的那些参数
- 会根据括号的参数生成对应的属性
- 会根据val和var关键字来生成setter、getter方法
- 关键字init:init{}它被称作是初始化代码块(Initializer Block),它的作用是为了Primary Constructor服务的,由于Primary Constructor是放置在类的首部,是不能包含任何初始化执行语句的,这是语法规定的,那么这个时候就有了init的用武之地,我们可以把初始化执行语句放置在此处,为属性进行赋值。
var和val关键字:var表示该属性可以被修改;val表示该属性不能被修改
class Person(val name: String) //name属性不可修改
---编译后---
public final class Person {
//1. 生成name属性
@NotNull
private final String name;
//2. 生成getter方法
//由于name属性不可修改,所以不提供name的setter方法
@NotNull
public final String getName() {
return this.name;
}
//3. 生成构造函数
public Person(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
}
}
如果我们把 name修饰符改成var,编译后会生成getter和setter方法,但是不会有final关键字来修饰name属性
如果这个name不用 var 也不用val修饰, 那么不会生成属性,自然也不会生成 getter 和 setter方法。不过可以在 init代码块 里进行初始化, 否则没有什么意义。
class Person(name: String) {
//会生成getter和setter方法
var name :String? =null
//init代码块会在构造方法里执行
init {
this.name = name
}
}
----编译后
public final class Person {
@Nullable
private String name;
@Nullable
public final String getName() {
return this.name;
}
public final void setName(@Nullable String var1) {
this.name = var1;
}
public Person(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
}
}
从上面的代码可知,init代码块 的执行时机是构造函数被调用的时候,编译器会把init代码块里的代码copy到构造函数里。
如果有多个构造函数,那么每个构造函数里都会有init代码块的代码,但是如果构造函数里调用了另一个重载的构造函数,init代码块只会被包含在被调用的那个构造函数里。
说白了,构造对象的时候,init代码块里的逻辑只有可能被执行一次。
二:class className constructor([var/val] property: Type…)
该种方式和上面是等价的,只是多加了constructor关键字而已
什么时候constructor可以省略:
- 在构造函数不具有注释符或者默认的可见性修饰符时,constructor关键字可以省略。
- 默认的可见性修饰符时public。可以省略不写。
例:
// 类似下面两种情况的,都必须存在constructor关键字,并且在修饰符或者注释符后面。
class Test private constructor(num: Int){
}
class Test @Inject constructor(num: Int){
}
三:类似Java的方式声明构造函数
不在类名后直接声明构造函数 ,在类的里面再声明构造函数。我们把这样的构造函数称之为 secondary constructor
class Person {
var name: String? = null
var id: Int = 0
constructor(name: String) {
this.name = name
}
constructor(id: Int) {
this.id = id
}
}
primary constructor 里的参数是可以被var/val修饰,而 secondary constructor 里的参数是不能被var/val修饰的
secondary constructor 用的比较少,用得最多的还是 primary constructor
**
四:同时存在主构造函数和二级构造函数时的情况
如果类具有主构造函数,则每个辅助构造函数需要通过另一个辅助构造函数直接或间接地委派给主构造函数。 使用this关键字对同一类的另一个构造函数进行委派:
fun main(args: Array<String>) {
var test1 = Test(1)
var test2 = Test(1,2)
}
// 这里是为了代码清晰,故而没有隐藏constructor关键字
class Test constructor(num: Int){
init {
println("num = $num")
}
constructor(num : Int, num2: Int) : this(num) {
println(num + num2)
}
}
输出结果为:
num = 1
num = 1
3
说明:二级构造函数中的参数1(num),是委托了主构造函数的参数num。
可以看出,当实例化类的时候只传1个参数的时候,只会执行init代码块中的代码。当传2个参数的时候,除了执行了init代码块中代码外,还执行了二级构造函数中的代码。