多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# 属性与字段 ## 声明属性 Kotlin 中的类可以有属性。`var` 关键字声明可变量或是使用 `val` 关键字声明为只读。 ``` kotlin public class Address { public var name: String = ... public var street: String = ... public var city: String = ... public var state: String? = ... public var zip: String = ... } ``` 要使用一个属性,只需简单地通过名称提交给它,如同 Java 中的字段一样: ``` kotlin fun copyAddress(address: Address): Address { val result = Address() // Kotlin 不需要 new result.name = address.name // 已经调用了访问器 result.street = address.street // ... return result } ``` ## Getter 和 Setter 完整的属性声明语法是 ``` kotlin var <propertyName>: <PropertyType> [= <property_initializer>] [<getter>] [<setter>] ``` 初始器、getter 和 setter 都是可选的。如果属性类型可以从初始器推断出或是从基类成员覆盖的话,那么也是可选的。 示列: ``` kotlin var allByDefault: Int? // error: explicit initializer required, default getter and setter implied var initialized = 1 // has type Int, default getter and setter ``` 两种区别只读属性声明与可变属性的完整的语法为:以 `val` 开始代替 `var` 并且不允许有 setter: ``` kotlin val simple: Int? // 拥有 Int 属性,默认的 getter,必须要构造器中初始化 val inferredType = 1 // 拥有 Int 属性和一个默认的 getter ``` 我们可以自定义访问器,非常类似于普通函数,就在属性声明下面。这里是一个自定义 getter 的示例: ``` kotlin val isEmpty: Boolean get() = this.size == 0 ``` 一个自定义 setter 是这样的: ``` kotlin var stringRepresentation: String get() = this.toString() set(value) { setDataFromString(value) // parses the string and assigns values to other properties } ``` 按照约定,setter 的参数是 `value`,但如果你喜欢你可以选择一个不同的名称。 如果你需要改变一个访问器的可见性或者要注释它,但不需要改变默认的实现,你可以定义访问器但不定义它的函数体: ``` kotlin var setterVisibility: String = "abc" // 必须初始化,不能是空类型 private set // setter 是私有的并拥有默认的实现 var setterWithAnnotation: Any? @Inject set // 用 Inject 注释 setter ``` ### 后台字段 Kotlin 中的类不能有字段。然而有时在使用自定义访问器时它有必要有一个后台字段。对于这些需求,Kotlin 自动提供一个后台字段,可以使用 `field` 标识访问: ``` kotlin var counter = 0 // the initializer value is written directly to the backing field set(value) { if (value >= 0) field = value } ``` `field` 标识只能在属性的访问器中使用。 编译器在看到访问器的函数体,如果它们使用了后台字段(或者访问器并非默认实现),就会创建一个后台字段,否则不会。 举例,下面的案例则不会有后台字段: ``` kotlin val isEmpty: Boolean get() = this.size == 0 ``` ### 后台属性 如果你要做的事不适合“隐式后台字段”的方案,你仍然可以退一步,用**后台属性**: ``` kotlin private var _table: Map<String, Int>? = null public 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` 修饰符标记为_编译期常量_。这样的属性需要满足下列要求: * 一个对象的顶层 * 初始化为 `String` 类型或是原始类型 * 没有自定义 getter 这样的属性可以在注解中使用: ``` kotlin const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated" @Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... } ``` ## 延迟初始化属性 一般地,声明为不可为空类型的属性必须在构造器中初始化。然而公平地说这样常常不太方便。比如说,改改可以通过依赖注入初始化,或者在单元测试中的 setup 方法。在这种情况下,你不能在构造器中提供一个非空的初始化,但你还是要避免在类中引用这个属性时的空检测。 这种情况下,你可以用 `lateinit` 修饰符标记该属性: ``` kotlin public class MyTest { lateinit var subject: TestSubject @SetUp fun setup() { subject = TestSubject() } @Test fun test() { subject.method() // dereference directly } } ``` 这个修饰符只能用在类内部声明的 `var` 属性上(不是主构造体),并且只能在改改不能有一个自定义的 getter 或 setter 时。属性的类型必须为非空,而且它绝不能是原始类型。 在一个 `lateinit` 属性被初始化前访问它会抛出一个特殊的异常来清楚地指明它初访问过而事实上它还没有被初始化。 ## 覆盖属性 查看 [覆盖成员](classes.html#overriding-members) ## 属性委托 许多通常的属性种类会简单地从后台字段读取(也可能是写入)。另外,有自定义 getter 和 setter 的可以实现一些属性的行为。介于两者之间,这是一个属性确定的通用工作方式。不多的例子:延迟值,通过一个给定的 key 从映身中读取,访问一个数据库,在访问时通知监听器等等。 这样的通用行为可以使用_属性委托_实现。更多的信息请看[这里](delegated-properties.html)