Kotlin学习笔记——属性与字段

  • 在Kotlin中,属性和字段可以使用关键字var(可变属性)和关键字val(不可变、只读属性), var定义的属性默认有gettersetterval定义的属性只有getter属性。

属性的定义

  • 定义属性的完整语法如下:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

// 中文版说明
var <属性名>[: <属性类型>] [= <属性初始化器>]
    [<getter>]
    [<setter>]
  • 在属性定义过程中,属性初始化器、gettersetter都是可选的,但是如果属性没有初始化器,必须在构造函数或者初始化块中对属性进行初始化
class Teacher : Person {
    // age属性未定义初始化器
    var age: Int

    init {
        // 在初始化块中初始化
        age = 0
    }

    constructor(name: String) : super(name) {
        // 在构造函数中初始化
        age = 0
    }
}

没有初始化器的属性,只需要在初始化块init和构造函数这两者选其一初始化即可。

  • 如果需要编写自己的gettersetter,并在内部实现对属性进行赋值或者返回属性的值,切忌直接使用属性,应该使用幕后字段,否则会出现无限循环调用导致StackOverflowError
  • 在 Kotlin 类中不能直接声明字段。然而,当一个属性需要一个幕后字段时,Kotlin 会自动提供。
  • 幕后字段只能在属性的访问器(gettersetter)中使用
  • 幕后字段通过field进行访问
class Teacher : Person {
    // age属性未定义初始化器
    var age: Int
    get() = field // 如果此处使用return this.age,会导致StackOverflowError
    set(value) {
        // 如果此处使用this.age = value,会导致StackOverflowError
        field = value
    }

    init {
        // 在初始化块中初始化
        age = 0
    }

    constructor(name: String) : super(name) {
        // 在构造函数中初始化
        age = 0
    }
}
  • 如果这套“隐式的幕后字段”方案不符合你的需求,那么总可以使用幕后属性(backing property)
class Teacher : Person {
    // age属性未定义初始化器
    var age: Int
    get() = field
    set(value) {
        field = value
    }

    var _addr: String? = null // 幕后属性
    val addr: String
    get() {
        if(null == _addr) {
            _addr = "this is test backing property"
        }
        return _addr ?: throw AssertionError("Set to null by another thread")
    }

    init {
        // 在初始化块中初始化
        age = 0
    }

    constructor(name: String) : super(name) {
        // 在构造函数中初始化
        age = 0
    }
}

编译期常量

编译期常量可以在注解中使用

已知值的属性可以使用 const 修饰符标记为编译期常量。 这些属性需要满足以下要求:

  • 位于顶层或者是 object 声明 或 companion object 的一个成员
  • String 或原生类型值初始化
  • 没有自定义 getter
// 编译期常量,必须位于顶层,不能放到类内部
const val TYPE_HEADER: Int = 1

class Teacher : Person {

    // age属性未定义初始化器
    var age: Int
    get() = field
    set(value) {
        field = value
    }

    var _addr: String? = null // 幕后属性
    val addr: String
    get() {
        if(null == _addr) {
            _addr = "this is test backing property"
        }
        return _addr ?: throw AssertionError("Set to null by another thread")
    }


    init {
        // 在初始化块中初始化
        age = 0
    }

    constructor(name: String) : super(name) {
        // 在构造函数中初始化
        age = 0
    }
}

延迟初始化属性与变量

一般地,属性声明为非空类型必须在构造函数中初始化。 然而,这经常不方便。例如:属性可以通过依赖注入来初始化, 或者在单元测试的 setup 方法中初始化。 这种情况下,你不能在构造函数内提供一个非空初始器。 但你仍然想在类体中引用该属性时避免空检查。

为处理这种情况,你可以用lateinit修饰符标记该属性:

public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method()  // 直接解引用
    }
}

该修饰符只能用于在类体中的属性(不是在主构造函数中声明的 var 属性,并且仅当该属性没有自定义 getter 或 setter 时),而自 Kotlin 1.2 起,也用于顶层属性与局部变量。该属性或变量必须为非空类型,并且不能是原生类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值