一: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代碼塊中代碼外,還執行了二級構造函數中的代碼。