Kotlin学习之类与对象篇—属性与字段

属性的声明

Kotlin类中可以有属性,属性可以被声明为可变属性,用var关键字修饰;或者声明为只读属性,用val修饰。

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

要使用一个属性,可以直接通过它的名字调用它:

fun copyAddress(address: Address): Address {
    val result = Address() 
    result.name = address.name 
    result.street = address.street
    // ...
    return result
}


Getters 和 Setters

声明一个属性的完整语法如下:

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

其中initializer, gettersetter都是可选项。如果属性的类型可推断(或者可以从getter的返回值类型推断),propertyType也是可选项。

例子:

var allByDefault: Int? // 报错: 属性未被初始化
var initialized = 1 // 属性 type 为 Int, 生成默认 getter 和 setter

只读属性的声明和可变属性有些不一样,它没有setter,毕竟只读。

我们能自定义存取器,下面是一个自定义的getter:

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

一个自定义的setter如下:

var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value) 
    }

方便起见,setter参数的名字是value,但这并不是关键字,你也可以给参数起其它名字。

如果需要改变存取器的可见性或者注释它,可以只定义存取器而不定义其实现,如下:

var setterVisibility: String = "abc"
    private set // private 的 setter,拥有默认实现

var setterWithAnnotation: Any? = null
    @Inject set // 被Inject注释的setter
(1). Backing Fields

这个不好翻译,看到有人译作幕后字段,也有人译作影子字段,它的作用是在存取器中代指属性本身。

Kotlin中不支持直接声明字段。然而,当一个属性需要backing field的时候,Kotlin会自动为它提供。backing field在存取器中用标识符field引用。

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

注意:field标识符只能在属性的存取器中被使用。

当一个属性默认实现至少一个存取器方法(getter/setter)或者在自定义存取器中使用field关键字引用的时候,它就会生成一个backing field。下面这个例子就没有生成backing field:

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

如果backing field无法满足需求,那么可以使用backing properties:

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来标记。编译时常量需要满足以下要求:
- 位于顶层 或者 是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方法,自从Kotlin 1.2,也可用于顶层的属性和局部变量。属性和变量必须为非空,而且不能是原始数据类型。

访问一个还没被初始化的lateinit属性将会抛出错误。

检查一个 lateinit var 是否已被初始化(自从kotlin 1.2)

要检查一个lateinit var 是否已被初始化,可以在该属性的引用上使用.isInitialized方法。

if (foo::bar.isInitialized) {
    println(foo.bar)
}

此检测仅对可词法级访问的属性可用,即声明位于同一个类型内、位于其中一个外围类型中或者位于相同文件的顶层的属性。

上面这段话不是特别明白 … …

猜你喜欢

转载自blog.csdn.net/chenrenxiang/article/details/80915836