Kotlin入门-必会,属性篇

在这里插入图片描述

前言

本文是基础,需要通篇看。

除了文字版本,也有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()
}

后续我们会讲到

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章