在 Kotlin 中,構造方法分為主構造方法(Primary Constructor)和次構造方法(Secondary Constructor)。
1 主構造方法
主構造方法是類的核心構造方法,直接在類頭聲明,位于類名之后。
1.1 基本語法
class Person constructor(val name: String, val age: Int) {// 類體
}
如果主構造方法沒有注解或可見性修飾符,constructor
關鍵字可以省略:
class Person(val name: String, val age: Int) {// 類體
}
1.2 特點
1.2.1 屬性聲明
參數直接作為屬性:可通過 val
或 var
將主構造函數的參數聲明為類的屬性。
class Person(val name: String, age: Int) {// name 是屬性,age 是構造方法參數
}
1.2.2 構造方法參數的作用域
主構造方法的參數可以在類體中直接使用,但未聲明 val
/ var
的參數僅在 init
代碼塊和屬性初始化器中可見。
class User(private val id: String, name: String, age: Int) {// id 是類屬性,在任何地方都可以用// name、age 僅在 init 代碼塊和屬性初始化器中可見val displayName = "[$id] $name"init {println("age = $age")}
}
屬性初始化器是用于在聲明屬性時直接賦值的語法,它允許在類體或主構造函數中直接為屬性設置初始值。
屬性初始化器可以在以下兩種場景中使用:
- 主構造函數中聲明屬性并初始化;
- 類體中可以直接初始化屬性;
// 通過主構造函數參數聲明屬性并初始化
class User(val name: String = "Unknown", var age: Int = 0)class User {val name: String = "Unknown" // 屬性初始化器val age: Int = 0 // 屬性初始化器
}
1.2.3 初始化代碼
使用 init
代碼塊執行額外的初始化邏輯。
class User(name: String, age: Int) {val formattedName: Stringinit {formattedName = "Mr./Ms. $name"println("Person initialized: $formattedName")}
}
1.2.4 可見性修飾符
主構造函數的可見性默認是 public
,可顯式指定:
class User private constructor(val name: String) // 私有構造方法
2 次構造方法(Secondary Constructor)
次構造方法通過 constructor
關鍵字在類體內定義,必須直接或間接調用主構造函數。
2.1 基本語法
class Person(val name: String) {var age: Int = 0// 次構造方法必須直接或間接委托給主構造方法constructor(name: String, age: Int) : this(name) {this.age = age}
}
2.2 注意事項
2.2.1 必須委托
每個次構造函數必須通過 this()
調用主構造函數或其他次構造函數,確保所有初始化路徑都經過主構造函數:
class User {constructor(name: String) : this(name, 0) // 錯誤:沒有主構造方法constructor(name: String, age: Int) // 如果沒有主構造方法,次構造方法無需委托
}
2.2.2 初始化順序
主構造函數的參數初始化 —> init
代碼塊 —> 次構造函數體:
class User(val name: String) {init {println("主構造方法初始化")}constructor(name: String, age: Int) : this(name) {println("次構造方法執行")}
}fun main() {User("Eileen", 34)
}// 主構造方法初始化
// 次構造方法執行
2.2.3 避免與主構造方法參數沖突
次構造方法的參數名應避免與主構造方法的屬性名重復:
class User(val name: String) {constructor(name: String, age: Int) : this(name) {// 此處的 name 參數會屏蔽類的 name 屬性}
}
3 初始化順序
- 主構造函數參數初始化;
- 類屬性按聲明順序初始化;
init
塊按出現順序執行;- 次構造函數執行;
class User(val name: String = "Eileen") {val a = println("a 初始化")init {println("init 1")}val b = println("b 初始化")init {println("init 2")}
}fun main() {User()
}// a 初始化 -> init 1 -> b 初始化 -> init 2
4 默認參數替代次構造方法
Kotlin 支持構造函數參數默認值,可減少構造函數的數量:
class User(val name: String,val age: Int = 0, // 默認參數val country: String = "Unknown"
)fun main() {val user1 = User("Eileen")val user2 = User("Eileen", 34)val user3 = User("Eileen", 34, "China")
}
Java 互操作性:如果需要在 Java 中調用帶默認參數的構造方法,需添加 @JvmOverloads
注解:
class Person @JvmOverloads constructor(val name: String,val age: Int = 0,val country: String = "Unknown"
)
5 繼承中的構造方法
5.1 子類必須初始化父類構造方法
子類的主構造方法需初始化父類的主構造方法:
open class User(val name: String)class Student(name: String) : User(name)
如果父類只有次構造方法(無主類構造方法),子類需通過 super
調用父類的某個次構造方法:
open class User {constructor(name: String) {}
}class Student : User {constructor(name: String) : super(name)
}
5.2 抽象類的構造方法
抽象類的構造函數可由子類實現:
abstract class User(val name: String)class Student(name: String, val age: Int) : User(name)
6 數據類中的構造方法
數據類的主構造方法必須至少有一個參數,且所有的參數必須標記為 val
或 var
:
7 總結
場景 | 注意事項 |
---|---|
主構造方法 | 參數可聲明為屬性,初始化順序嚴格,支持默認參數替代次構造方法 |
次構造方法 | 必須委托主構造方法,代碼體在類初始化后執行 |
繼承 | 子類必須初始化父類構造方法,優先使用主構造方法 |
默認參數 | 替代次構造方法,需要 @JvmOverloads 支持 Java 調用 |
初始化順序 | init 塊和屬性初始化按照代碼順序執行,次構造方法體最后執行 |