目錄
定義類
計算屬性
防範靜态條件
類的初始化
主構造函數
主構造函數中定義屬性
次構造函數
預設參數
初始化塊
初始化順序
延遲初始化
惰性初始化
初始化陷阱
定義類
針對我們定義的每一個屬性,Kotlin都會産生一個field、一個getter和setter,field用來存儲屬性資料,我們不能直接定義field,Kotlin會封裝field,保護它裡面的資料,隻暴露給getter和setter使用。屬性的getter方法決定如何讀取屬性值,每個屬性都有它的getter方法,setter方法決定你如何給屬性指派,是以隻有可變屬性才會有setter方法,盡管Kotlin會自動提供預設的getter和setter,但在需要控制如何讀取屬性資料時,我們也可以自定義它們。

import kotlin.math.absoluteValue
fun main() {
var p = Player()
p.name = " Honey "
println(p.name)
p.age = 18
println(p.age)
}
class Player {
var name = "Honey"
get() = field.capitalize()
set(value) {
field = value.trim()
}
var age = 17
get() = field.absoluteValue
set(value) {
field = value - 1
}
}
計算屬性
計算屬性是 通過一個覆寫的get和set運算符來定義的,這時field就不需要了。
class Player2 {
var rolledValue = 0
get() = (1..66).shuffled().first()
}
防範靜态條件
如果一個類屬性既可空又可變,那麼引用它之前你必須保證它非空,一個辦法是用also标準函數。
class Player3 {
var words: String? = null
fun sayWords() {
words?.also {
println("Hello,${it.capitalize()}")
}
}
}
fun main() {
val p = Player3()
p.sayWords()
p.words = "Kitty"
p.sayWords()
}
類的初始化
主構造函數
我們在Player類的定義中定義一個主構造函數,使用臨時變量為Player的各個屬性提供初始值,在Kotlin中,為便于識别,臨時變量(包含僅引用一次的參數),通常都會以下劃線開頭的名字命名。
class Player(_name: String, _age: Int, _score: Double) {
var name = _name
var age = _age
var score = _score
}
fun main() {
var p = Player("Honey", 17, 100.0)
println("${p.name}今年${p.age}歲了,本學期成績${p.score}分,好棒鴨!")
}
主構造函數中定義屬性
class Player(_name: String, var age: Int, var score: Double) {
var name = _name
}
fun main() {
var p = Player("Honey", 17, 100.0)
println("${p.name}今年${p.age}歲了,本學期成績${p.score}分,好棒鴨!")
}
次構造函數
有主就有次,對應主構造函數的是 次構造函數,我們可以定義多個次構造函數來配置不同的參數組合。
class Player4(_name: String, var age: Int, var score: Double) {
var name = _name
constructor(name: String) : this(name, 10, 98.0)
}
fun main() {
var p2 = Player4("Luffy")
println("${p2.name}今年${p2.age}歲了,本學期成績${p2.score}分,好棒鴨!")
}
使用次構造函數也可以定義初始化代碼邏輯:
class Player5(_name: String, _age: Int, _score: Double) {
var name = _name
var age = _age
var score = _score
constructor(_name: String, _age: Int) : this(_name, _age, 60.0) {
this.name = _name.toUpperCase()
this.age = _age - 1
this.score += 6
}
}
fun main() {
var p = Player5("Lucky", 18, 92.0)
println(p.name)
println(p.age)
println(p.score)
}
預設參數
定義構造函數時,可以給構造函數參數指定預設值,如果使用者調用時不提供值參,就使用這個預設值。
class Player6(_name: String, var age: Int = 16, var score: Double = 0.0) {
var name = _name
}
fun main() {
var p = Player6("Kitty")
println("${p.name}今年${p.age}歲了,本學期成績${p.score}分。")
}
初始化塊
初始化塊可以設定變量和值,以及執行有效性檢查,如檢查傳給某構造函數的值是否有效,初始化塊代碼會在構造類執行個體時執行。
class Student(_name: String, _age: Int) {
var name = _name
var age = _age
init {
require(age > 0) { "Age is illegal" }
require(name.isNotBlank()) { "Student must have a name." }
}
}
fun main() {
var stu = Student("",-1)
}
初始化順序
- 主構造函數裡面聲明的屬性
- 類級别的屬性指派
- init初始化塊裡的屬性指派和函數調用
- 次構造函數裡的屬性指派和函數調用
class Student2(_name: String, var age: Int) {
var name = _name
get() = field.capitalize()
set(value) {
field = value.trim()
}
var hobby: String
val subject: String
init {
println("initializing ...")
subject = "math"
hobby = "listen music"
}
constructor(_name: String) : this(_name, 17) {
hobby = "play guitar"
}
}
fun main() {
var stu = Student2("Lucky")
}
延遲初始化
- 使用lateinit關鍵字相當于做了一個約定:在用它之前負責初始化。
- 隻要無法确認lateinit變量是否完成初始化,可以執行isInitialized檢查。
class Chef() {
lateinit var ingredients: String
fun ready() {
ingredients = "rice"
}
fun cook() {
if (::ingredients.isInitialized) println(ingredients)
}
}
fun main() {
var chef = Chef()
chef.ready()
chef.cook()
}
惰性初始化
class LazyInit(_name: String) {
var name = _name
val config by lazy { loadConfig() }
private fun loadConfig(): String {
println("loading ...")
return name.capitalize()
}
}
fun main() {
val result = LazyInit("luffy")
println(result.name)
Thread.sleep(5000)
println(result.config)
}
初始化陷阱
- 在使用初始化塊時,順序非常重要,必須保證初始化塊中的所有屬性均已完成初始化。
Kotlin學習筆記(五) - 下面這段代碼編譯是沒有問題的,因為編譯器看到name屬性已在init中初始化了,但代碼運作會抛出空指針異常,因為name屬性還沒有指派的時候,firstLetter函數就把name拿去用了。
class InitTrap { val name: String private fun firstLetter() = name[0] init { println(firstLetter()) name = "Honey" } } fun main() { val initTrap = InitTrap() }
- 因為編譯器看到所有屬性都初始化了,是以代碼編譯沒問題,但運作結果卻是null,問題出在哪?在用initPlayerName函數初始化playerName時,name屬性還未完成初始化。
class InitTrap(_name: String) { val playerName: String = initName() var name = _name private fun initName() = name } fun main() { val initTrap = InitTrap("Honey") println(initTrap.playerName) }