Kotlin 的屬性看起來與 Java 的欄位相似。然而,它們擁有不同的概念。
//Kotlin property
var name:String?=null
//Java field
String name=null;
即使以相同的方式使用,Kotlin 的屬性也能實現更多功能。
屬性宣告的完整語法
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
自定義 Get, Set ( var )
var name: String? = null
get() = field?.toUpperCase()
set(value) { if(!value.isNullOrBlank()) { field = value }
自定義 Get ( val )
val fullName: String?
get() = "$name $surname"
後備欄位 (Backing Fields)
欄位無法自定義宣告。不過,當屬性需要後備欄位時,系統會自動建立。此後備欄位可以在存取器中透過 field 識別字進行參照。
var counter = 0 // 初始化器直接賦值給後備欄位
set(value) {
if (value >= 0)
field = value
// counter = value // 錯誤 StackOverflow:使用實際名稱 'counter' 會導致 setter 遞迴呼叫
}
與函數有何不同?
在 Kotlin 中,屬性代表該物件的狀態,而函數則代表該物件的行為。
// 屬性
val isEmpty: Boolean
get() = amount == 0
// 函數
fun isEmpty(): Boolean {
return amount == 0
}
讓我們思考一下。
總是使用屬性的方式比較好嗎?
class FruitBucket(
val price: Int,
val discount: Int,
var amount: Int
) {
val isEmpty: Boolean
get() = amount == 0
val salePrice: Int
get() = price - discount
}
若要改善上述程式碼,可以修改如下:
因為每次存取時都進行計算是沒有效率的。
class FruitBucket(
val price: Int,
val discount: Int,
var amount: Int
) {
val isEmpty: Boolean
get() = amount == 0
val salePrice: Int = price - discount
}
可以在不持有資料的情況下使用該屬性。
基於某些原因,即便無法使用 Date 型別,也可以利用 Kotlin 的屬性功能,不直接儲存 Date 型別的資料,而是利用其他數值來使用 Date 型別的值。(例如在進行序列化等操作時,僅持有 millis 的值,使用時再轉換為 Date 型別的形式)
var date: Date
get() = Date(millis)
set(value) {
millis = value.time
}
屬性不僅是單純的欄位,也可以作為存取器使用。
val Context.preferences:SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(this)
val Context.inflater:LayoutInflater
get() = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val Context.notificationManager:NotificationManager
get() = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
屬性功能是萬能的嗎?
包含迴圈、重複等特定邏輯,或是注入演算法部分 的屬性,並非正確的使用方式。
// 不要這樣做!
val Tree<Int>.sum:Int
get() = when (this) {
is Leaf -> value
is Node -> left.sum + right.sum
}
屬性 (Property) 通常應該僅用於表示狀態或進行設定。
以下提供建議使用函數而非屬性的情況:
- 複雜度高於 O(1) 的情況(計算成本較高時)
- 包含除了簡單操作(如記錄日誌、更新元素等)以外的業務邏輯時
- 連續兩次呼叫成員卻輸出不同結果時
-
會產生類似
Int.toDouble()這種慣例表達式的重複時 - 在 Getter 中變更屬性狀態時
參考資料
- https://kotlinlang.org/docs/properties.html#backing-fields
- Effective Kotlin