屬性
以下是 Kotlin 屬性(Property) 的基礎概念:var
和 val
的區別、自訂存取器、延遲初始化(lateinit
和 lazy
)、const
常數等。
1. 基本概念
定義屬性
Kotlin 中的屬性可以用以下兩種方式定義:
var
:可變屬性(可讀寫)。val
:不可變屬性(唯讀)。
範例:var
和 val
class Person {
var name: String = "Default Name" // 可讀寫
val age: Int = 18 // 唯讀
}
fun main() {
val person = Person()
println(person.name) // 輸出:Default Name
person.name = "Alice"
println(person.name) // 輸出:Alice
// person.age = 20 // 錯誤:`val` 屬性不可修改
println(person.age) // 輸出:18
}
2. 預設 getter
和 setter
Kotlin 中,所有屬性都自動生成預設的 getter
和 setter
:
val
(不可變屬性):僅有getter
。var
(可變屬性):同時擁有getter
和setter
。
範例:
class Person {
var name: String = "Default Name" // 自動生成 getter 和 setter
val age: Int = 18 // 只有 getter
}
fun main() {
val person = Person()
// 使用預設的 getter
println(person.name) // 輸出:Default Name
println(person.age) // 輸出:18
// 使用預設的 setter
person.name = "Alice"
println(person.name) // 輸出:Alice
// person.age = 25 // 錯誤:`val` 屬性無法修改
}
3. 自訂 getter
和 setter
Kotlin 允許自訂屬性的 getter
和 setter
,用於在屬性讀取或寫入時執行額外邏輯。
語法
var 屬性名稱: 資料型別 = 初始值
get() = // 自訂 getter
set(value) {
// 自訂 setter
}
範例:
class Person {
var name: String = "Default Name"
get() {
println("Getter called for name")
return field // 使用 field 取得屬性值
}
set(value) {
println("Setter called with value: $value")
field = value // 使用 field 設定屬性值
}
var age: Int = 18
set(value) {
if (value >= 0) {
field = value // 僅允許非負數
} else {
println("Age cannot be negative")
}
}
}
fun main() {
val person = Person()
// 測試自訂 getter 和 setter
println(person.name) // 輸出:Getter called for name -> Default Name
person.name = "Alice" // 輸出:Setter called with value: Alice
println(person.name) // 輸出:Getter called for name -> Alice
person.age = -5 // 輸出:Age cannot be negative
println(person.age) // 輸出:18
}
關鍵字:field
的作用
field
是後備字段(Backing Field):- 存儲屬性的實際值。
- 當屬性有自訂的
getter
和setter
時,需用field
訪問或設置屬性的內部值。
為什麼需要 field
?
如果在 getter
或 setter
中直接使用屬性名稱(如 name
),會導致遞迴調用自己,最終導致程式崩潰。
錯誤範例:沒有使用 field
class Person {
var name: String = "Default Name"
get() {
return name // 錯誤:導致遞迴調用
}
set(value) {
name = value // 錯誤:導致遞迴調用
}
}
正確範例:使用 field
class Person {
var name: String = "Default Name"
get() = field // 正確使用
set(value) {
field = value // 正確使用
}
}
4. 計算屬性
計算屬性不需要存儲數據,每次訪問時動態計算值。計算屬性僅有 getter
,無 setter
。
範例:
class Rectangle(val width: Int, val height: Int) {
val area: Int
get() = width * height // 計算屬性值
}
fun main() {
val rectangle = Rectangle(5, 10)
println("Area: ${rectangle.area}") // 輸出:Area: 50
}
5. 延遲初始化屬性
Kotlin 提供兩種方式延遲初始化屬性:lateinit
和 lazy
。
5.1 lateinit
- 用於延遲初始化可變屬性(
var
)。 - 必須檢查是否已初始化,否則會拋出異常。
- 常用於類別初始化時無法確定值的情況。
範例:
class Person {
lateinit var name: String
fun initializeName(value: String) {
name = value
}
fun printName() {
if (::name.isInitialized) {
println("Name: $name")
} else {
println("Name is not initialized")
}
}
}
fun main() {
val person = Person()
person.printName() // 輸出:Name is not initialized
person.initializeName("Alice")
person.printName() // 輸出:Name: Alice
}
5.2 lazy
- 用於延遲初始化唯讀屬性(
val
)。 - 第一次訪問時執行初始化邏輯,且僅執行一次。
範例:
class Person {
val greeting: String by lazy {
println("Initializing greeting...")
"Hello, Kotlin!"
}
}
fun main() {
val person = Person()
println("Before accessing greeting")
println(person.greeting) // 第一次訪問時初始化,輸出:Initializing greeting... -> Hello, Kotlin!
println(person.greeting) // 輸出:Hello, Kotlin!
}
6. 常數(const
)
const
修飾的屬性
- 必須在
object
或companion object
中定義。 - 必須是基本型別(
String
、Int
等)。 - 編譯時期常數,無法修改。
範例:
class Constants {
companion object {
const val MAX_USERS = 100
}
}
fun main() {
println("Max users: ${Constants.MAX_USERS}") // 輸出:Max users: 100
}
7. 屬性的可見性修飾符
Kotlin 提供以下可見性修飾符,用於控制屬性的存取範圍:
public
(預設):所有地方可見。private
:僅在類別內部可見。protected
:在類別及其子類別中可見。internal
:在同一模組內可見。
範例:可見性修飾符
open class Animal {
private var privateProp = "Private"
protected var protectedProp = "Protected"
internal var internalProp = "Internal"
public var publicProp = "Public"
fun printProps() {
println("$privateProp, $protectedProp, $internalProp, $publicProp")
}
}
class Dog : Animal() {
fun printDogProps() {
// println(privateProp) // 錯誤:private 無法存取
println(protectedProp) // 可存取
println(internalProp) // 可存取
println(publicProp) // 可存取
}
}
fun main() {
val dog = Dog()
dog.printDogProps()
// dog.privateProp // 錯誤:無法存取 private 屬性
// dog.protectedProp // 錯誤:無法存取 protected 屬性
println(dog.publicProp) // 輸出:Public
}
8. 擴展屬性
Kotlin 支援為現有類型定義擴展屬性。
範例:
val String.wordCount: Int
get() = this.split(" ").size
fun main() {
val text = "Kotlin is awesome"
println("Word count: ${text.wordCount}") // 輸出:Word count: 3
}