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
AD