在写Java时候我们对字段命名一般会用m
前缀,那么我们在写kotlin时候却没有用这种方式呢?因为kotlin中函数是一等公民,而且可以定义顶层属性和函数,官方也就不再推荐这样的命名规范。并且更多时候称呼kotlin中的变量定义为属性(properties
),而不用java中的字段(field
)。而kotlin使用"_"作为属性的前缀被称之为后备属性,先解读下官方文档进行学习。
后背字段 Backing fields
Kotlin的类不能有字段。 但是,有时在使用自定义访问器时需要有一个后备字段。 为了这些目的,Kotlin提供了可以使用字段标识符访问的自动备份字段:
var counter = 0 // the initializer value is written directly to the backing field
set(value) { if (value >= 0) field = value }
这里field
标识符只能在属性的set{} get{}
中访问。在自定义访问器通过field
标识符引用它,则将为属性生成后备字段。 在以下情况下,将不会有后备字段:
val isEmpty: Boolean
get() = this.size == 0
后备属性 Backing properties
Kotlin后备属性,下面是官方给的示例。
private var _table: Map<String, Int>? = null
val table: Map<String, Int>
get() {
if (_table == null) {
_table = HashMap() // Type parameters are inferred
}
return _table ?: throw AssertionError("Set to null by another thread")
}
我们经常会在一些代码中看见这样的写法,很多人会表示疑惑,同一个属性为何要用2个变量来持有。一般这些代码有如下特点:
- 私有的属性有
更多
可操作的性 - 对外暴露的属性有
更少
的可操作性
示例1:
class TestFragment: Fragment() {
private var _binding: VB? = null
protected val binding: VB
get() = requireNotNull(_binding) { "The property of binding has been destroyed." }
...
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
示例2:
class EffectViewModel: ViewModel() {
private val _effect = MutableSharedFlow<Effect>()
val effect: SharedFlow<Effect> by lazy { _effect.asSharedFlow() }
}
示例3:
class LiveDataViewModel: ViewModel() {
private val _liveData = MutableLiveData<Int>()
val liveData: LiveData<Int> = _liveData
}
后备属性可以更好的构建安全的数据流,因为缩紧了对数据的可操作权限,对外部只提供可读取的权限。这样可以保证数据的安全性,在UDF的数据流中更加容易定位问题和进行数据追踪。