前言
本文是基础,需要通篇看。
除了文字版本,也有Xmind版本 github地址
同样也带着几个问题来看:
① 幕后字段与幕后属性的区别
② val var什么区别
③ field字段什么时候不会有
④ 属性延迟初始化怎么实现?
⑤ 什么是编译期常量
本文目录如下
- 定义
- getter和setter
- var
- val
- getter和setter方法的权限
- 注意
- 幕后字段
- 什么时候没有field字段?
- 幕后属性
- 定义
- 对于 JVM 平台
- 幕后字段与幕后属性的区别
- 编译期常量
- 延迟初始化属性与变量
- 解决办法
- 适用范围
- 注意
- 检测一个 lateinit var 是否已初始化(自 1.2 起)
- 覆盖属性
- 委托属性
定义
val var是不是有一股JS的味道。
属性定义有两种
- var 可变
- val 不可变
属性引用
myInstance.myVar
getter和setter
格式是这样的
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
getter 和 setter 都是可选
有几个规则。
- val是不可变,so没有setter
var
可变的可赋值的变量
var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法
var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setter
val
不可变的不可赋值的变量
val simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化
val inferredType = 1 // 类型为 Int 类型,默认实现 getter
范例一: 修改了get方法
var lastName: String = "zhang"
get() = field.toUpperCase() // 将变量赋值后转换为大写
set
范例二: 修改了set方法
var no: Int = 100
get() = field // 后端变量
set(value) {
if (value < 10) { // 如果传入的值小于 10 返回该值
field = value
} else {
field = -1 // 如果传入的值大于等于 10 返回 -1
}
}
getter和setter方法的权限
setter方法:支持加属性 private protected 等
getter方法:的权限跟所处的变量一致
注意
field属性的作用域:
- 仅仅存在于当前属性的setter/getter方法中
- 就像当前属性的影子一样。
- 它是自动生成
范例三
class Person {
var lastName: String = "zhang"
get() = field.toUpperCase() // 将变量赋值后转换为大写
set
var color: String = "#ff0000"
set(value){ field = value }
get() = field
var number : Int = 100
var no: Int = 100
get() = field // 后端变量
set(value) {
if (value < 10) { // 如果传入的值小于 10 返回该值
field = value
} else {
field = -1 // 如果传入的值大于等于 10 返回 -1
}
}
var heiht: Float = 145.4f
private set
}
fun main(args: Array<String>) {
var person: Person = Person()
person.lastName = "wang"
println("lastName:${person.lastName}")
person.no = 9
println("no:${person.no}")
println("no:${person.number}")
person.color = ("#ffffff")
println("no:${person.color}")
println("no:${person.heiht}")
}
输出
lastName:WANG
no:9
no:100
no:#ffffff
no:145.4
幕后字段
用field标识符
在访问器中引用
当一个属性需要一个幕后字段时,Kotlin 会自动提供
var counter = 0 // 注意:这个初始器直接为幕后字段赋值
set(value) {
if (value >= 0) field = value
}
只能用在属性的访问器内
什么时候没有field字段?
field字段是需要特定的条件下才能产生的。部分情况不会产生。
- 不使用默认实现
- 且不通过 field 引用幕后字段
比如以下这种情况
val isEmpty: Boolean
get() = this.size == 0
幕后属性
定义
幕后属性是我们自己定义的,一般用于这种情况
- 对外表现为只能读,不能写
- 对内表现可以任意读写
相当于,一个变量即是 val 又是var,java可以通过函数实现
范例
class Demo{
val size get() = _size
private var _size:Int = 0
fun rise() {
_size = _size + 1
}
}
fun main(args: Array<String>) {
val demo = Demo()
println(demo.size)
demo.rise()
println(demo.size)
demo.rise()
println(demo.size)
}
输出
0
1
2
对于 JVM 平台
通过默认 getter 和 setter 访问私有属性会被优化
不会引入函数调用开销
幕后字段与幕后属性的区别
幕后字段:Kotlin内置提供给我们的
幕后属性:是我们设计类属性时的一种实现方法而已
编译期常量
使用 const 修饰符标记为 编译期常量
使用条件
- 位于顶层或者是 object 声明 或 companion object 的一个成员
- 以 String 或原生类型值初始化
- 没有自定义 getter
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
与java一样
常量可以用在注解中
延迟初始化属性与变量
一般地,属性声明为非空类型必须在构造函数中初始化
但是,你是通过注解,或者某setup函数来初始化的
你不能在构造函数内提供一个非空初始器
仍然想在类体中引用该属性时避免空检测
解决办法
用 lateinit 修饰符标记该属性
适用范围
- 非空类型
- 非原生类型
注意
在初始化前访问一个 lateinit 属性会抛出一个特定异常,
该异常明确标识该属性被访问及它没有初始化的事实。
检测一个 lateinit var 是否已初始化(自 1.2 起)
用 .isInitialized
if (foo::bar.isInitialized) {
println(foo.bar)
}
仅对可词法级访问的属性
覆盖属性
可以参考 《类篇》
委托属性
即delegate
用到了关键字 by
范例
class Example {
var p: String by Delegate()
}
后续我们会讲到