Kotlin学习之-5.2 属性和成员

来源:互联网 发布:苏联歌曲 知乎 编辑:程序博客网 时间:2024/06/02 05:02

Kotlin学习之-5.2 属性和成员

定义属性

Kotlin中,类可以有属性。它们既可以用var关键字定义成变量,也可以用val关键字定义成只读量。

class Address {    var name: String = ...    var street: String = ...    var city: String = ...    var state: String? = ...    var zip: String = ...}

使用一个属性,只要简单的用它的名字引用即可,就像Java中的成员一样。

fun copyAddress(address: Address): Address {    val result = Address() // Kotlin中没有'new' 关键字    result.name = address.name    result.street = address.street    //...    return result}

Getter 和Setter

定义属性完整语法结构如下:

var <propertyName>[: <PropertyType>] [= <property_initializer>]    [<getter>]    [<setter>]

构造器、getter 和setter都是可选项。如果属性的类型可以从构造器中推断出来的话,那么属性的类型也是可选的。示例如下:

var allByDefault: Int? // error: 需要显式的构造器。 有默认的getter 和settervar initialized = 1 // 类型是Int。   有默认的getter 和setter

只读属性的语法和变量属性的的语法有两点不同: 定义是用val 而不是var,并且没有setter 方法

val simple: Int? // 类型是Int,默认的getter方法,必须在构造函数中初始化。val inferredType = 1 // 类型是Int, 默认的getter方法。

我们可以在属性定义时自定义getter方法,和普通的函数非常像。示例如下:

val isEmpty: Boolean    get() = this.size == 0

自定义setter方法示例如下:

var stringRepresentation: String    get() = this.toString()    set(value) {        setDataFromString(value) // 解析字符串并且赋值给其他属性    }

一般情况,setter方法的参数名是value,但是也可以选择一个其他名字。
从Kotlin v1.1开始,如果属性类型可以从getter方法中推断出来,那么定义的时候可以省略属性类型。

val isEmpty() get() = this.size == 0 // 推断的类型Boolean

如果要更改函数的可见性或者添加注解,但是不需要干煸默认的实现,可以定义访问函数但不定义函数主体。

var setterVisibility: String = "abc"    private setvar setterWithAnnotation: Any? = null    @inject set

Backing Fields

Kotlin中,类没有成员。但是有时候使用自定义访问函数时也需要一个’backing field’。 在这些用法中,Kotlin提供一个自动的’backing field’, 它可以用field关键字来访问。

var counter = 0    set(value) {        if (value >= 0) field = value    }

field描述符只能用在属性的访问。

属性会产生一个’backing field’,如果属性有至少一个默认实现的访问函数,或者自定义的访问函数使用了field描述符来访问这个属性。
例如,下面的例子中没有没有’backing field’

val isEmpty: Boolean    get() = this.size == 0

Backing Properties

如果你需要做的事情不符合隐式backing field模式,那么可以使用backing property

private var _table: Map<String, Int>? = nullpublic val table: Map<String, Int>    get() {        if (_table == null) {            _table = HashMap() // 类型参数是推断出来的        }        return _table ?: throw AssertionError("Set to null by another thread")    }

总的来说,这和Java是一样的,使用默认的getter和setter方法访问私有的属性会被优化,从而没有函数调用的负担。

编译期常量

属性的值在编译期就确定的话,用const修饰符来表示成编译期常量。这样的属性需要满足如下要求。

  • 顶级的或者是一个object的成员
  • 是用String类型或者基础类型初始化的
  • 没有自定义getter方法

这样的属性可以在注解中使用

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }

延迟初始化的属性

一般情况,被定义成非空的属性必须在构造函数中初始化。但是,这样通常不太方便。例如,属性可以在依赖注入的时候被初始化,或者在单元测试的setup方法中初始化。 在这种情况,就不能再构造函数中提供一个非空的初始化器,但是你仍然想要在引用属性的时候避免空指针检查。

处理这种情况,需要用lateinit 描述符来标识这个属性。

public class MyTest {    lateinit var subject: TestSubject    @SetUp fun setup() {        subject = TestSubject()    }    @Test fun test() {        subject.method()    }}

延迟初始化lateinit 描述符只能用在var属性,并且属性不能有自定义的getter和setter。属性的类型必须是非空的,并且不能是基础类型。

在属性初始化之前,访问一个延迟初始化lateinit的属性,会抛出一个特殊的异常,该异常表明属性在访问的时候,还没有初始化。

代理属性

最常用的属性就是简单地读取或者写入backing field。 另外,使用自定义的getter和setter可以实现属性任意的操作。 除了这两种情况,还有一些常见的用法。例如,延迟初始化的值,用一个给定的关键字从一个map中获取对应的值,访问数据库,通知监听器等等。

这些常用的行为和操作可以通过库的方式来实现。这可以使用代理属性的方式来实现。


PS,我会坚持把这个系列写完,有问题可以留言交流,也关注专栏Kotlin for Android Kotlin安卓开发

原创粉丝点击