【Kotlin学习日记】Day9:属性和字段

大家好,我是William李梓峰,欢迎加入我的Kotlin学习之旅。
今天是我学习 Kotlin 的第九天,学习内容是 Properties and Fields - 属性和字段

之前,我用笔名小李君,后来发现,现在都2017年了,笔名太土了,还是直接真名吧,说不定以后会火了呢,就酱紫。

官方文档:

Properties and Fields - 属性和字段

Declaring Properties - 声明的属性

Classes in Kotlin can have properties.
These can be declared as mutable, using the var{: .keyword } keyword or read-only using the val{: .keyword } keyword.
类在 Kotlin 的世界里面可以有属性。
用 var 来声明可变的属性,用 val 来声明不可变的属性。

class Address {
    var name: String = ...
    var street: String = ...
    var city: String = ... 
    var state: String? = ...         //  我是可以为空的可变属性
    var zip: String = ...
}

To use a property, we simply refer to it by name, as if it were a field in Java:
怎么调取属性呢?直接像 JavaScript 那样直取就行了。不用通过 getter 或 setter :

fun copyAddress(address: Address): Address {
    val result = Address()        // there's no 'new' keyword in Kotlin
    result.name = address.name        // accessors are called
    result.street = address.street
    // ...
    return result
}

Getters and Setters - 不翻译了

The full syntax for declaring a property is
声明属性的完整语法糖就是酱紫:

// 括号都是可选的
var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

The initializer, getter and setter are optional. Property type is optional if it can be inferred from the initializer
(or from the getter return type, as shown below).
初始化代码,getter 和 setter 都是可写可不写。属性的类型也是可选的,只要它能够通过初始化代码推断出类型
(或从 getter 的返回类型中推断,就像下面演示的那样)。

Examples:

var allByDefault: Int? // error: explicit initializer required, default getter and setter implied
var initialized = 1 // has type Int, default getter and setter

The full syntax of a read-only property declaration differs from a mutable one in two ways: it starts with val instead of var and does not allow a setter:
val 没 setter:

val simple: Int? // has type Int, default getter, must be initialized in constructor
val inferredType = 1 // has type Int and a default getter

We can write custom accessors, very much like ordinary functions, right inside a property declaration. Here's an example of a custom getter:
我们可以写一个自定义的访问器,就像函数那样子,直接写在属性的下面。这里有个自定义 getter 访问器:

val isEmpty: Boolean
    get() = this.size == 0    // getter 写的是函数表达式,返回布尔值

A custom setter looks like this:
一个自定义的 setter 可以这么写:

var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value) // parses the string and assigns values to other properties
    }

By convention, the name of the setter parameter is value, but you can choose a different name if you prefer.
按照惯例,setter 的形参名都是 value,但是你可以写其他的名字(废话)。

Since Kotlin 1.1, you can omit the property type if it can be inferred from the getter:
自从 Kotlin 1.1 开始,你可以忽略属性的类型,只要它可以从 getter 中推断出来(上面讲过了啊):

val isEmpty get() = this.size == 0  // has type Boolean

If you need to change the visibility of an accessor or to annotate it, but don't need to change the default implementation,
you can define the accessor without defining its body:
如果你需要改变访问器的可访问性或注解的修饰,是并不需要改变原来 setter 或 getter 的默认实现的。
你可以直接定义访问器而不用写它的 “身体”(别误会,明明是大括号的代码实现,这纯粹是文化差异)。

var setterVisibility: String = "abc"
    private set // the setter is private and has the default implementation

var setterWithAnnotation: Any? = null
    @Inject set // annotate the setter with Inject

Backing Fields - 备用的字段(什么鬼)

Classes in Kotlin cannot have fields. However, sometimes it is necessary to have a backing field when using custom accessors. For these purposes, Kotlin provides an automatic backing field which can be accessed using the field identifier:
Kotlin 的类不可以拥有字段(只可以有属性,明明就是一个东西)。但是,有时候它还可以有个备用字段为自定义访问器所用。Kotlin 提供了一个自动备用字段,这种字段可以用字段的识别码去访问。(什么是识别码。。。)

var counter = 0 // the initializer value is written directly to the backing field
    set(value) {
        if (value >= 0) field = value    // field 就是代表 counter 本身
    }

The field identifier can only be used in the accessors of the property.
field 只可以用于 setter 或 getter,代表当前字段,类似 this 代表当前对象一样。

A backing field will be generated for a property if it uses the default implementation of at least one of the accessors, or if a custom accessor references it through the field identifier.
一个备用字段会生成出来,用于属性的访问器默认实现,或自定义访问器通过 field 识别码来调取。

For example, in the following case there will be no backing field:
例如,在这个下面这个例子中,就没有备用字段:

val isEmpty: Boolean
    get() = this.size == 0        // 如果是 get() = field 应该就是默认实现了

Backing Properties - 备用属性

If you want to do something that does not fit into this "implicit backing field" scheme, you can always fall back to having a backing property:
如果你想不用让大脑强制适应什么是“备用字段”这种新概念,你可以思考一下什么是“备用属性”。(更加烧脑了好吗。。)

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        if (_table == null) {
            _table = HashMap() // Type parameters are inferred
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }

In all respects, this is just the same as in Java since access to private properties with default getters and setters is optimized so that no function call overhead is introduced.
反正呢,别管这么多了,在 Java 的世界里面,private field 都是通过 public setter getter 访问的,这是最佳实践,同时也是约定俗成的老规矩。Kotlin 只是简化了这个过程。声明一个属性,就会同时生成对应的 setter getter(访问器),你可以直接重写任意访问器,可以在访问器里面通过 field 来访问当前字段。真是够啰里啰嗦的。

Compile-Time Constants - 编译时常量

Properties the value of which is known at compile time can be marked as compile time constants using the const modifier.
属性可以用 const 标记为编译时常量。

Such properties need to fulfil the following requirements:
这种属性需要满足下面的要求:

  • Top-level or member of an object
  • Initialized with a value of type String or a primitive type
  • No custom getter
  • 顶级对象或顶级成员
  • 用 String 类型或一个基本类型初始化
  • 没有自定义 getter

Such properties can be used in annotations:
这种属性可以直接用在注解上,爽了:

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"

@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }

Late-Initialized Properties - 延迟初始化属性

Normally, properties declared as having a non-null type must be initialized in the constructor.
通常来说,不为空的属性必须在构造器内初始化完毕。

However, fairly often this is not convenient.
但是,这样做并不是很方便。

For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In this case, you cannot supply a non-null initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class.
例如,属性可以通过依赖注入来初始化(Spring 要乱入),或者在单元测试的预备方法内初始化。在这种场景下,你不可以在构造器里面完成属性的初始化(因为调用完了构造器,属性还必须是空的,因为要注入啊,但不想直接写问号标明这些属性是可以为空的,明明就不想为空啊),但你却还想要在类体中调用属性时避免空值检查。这咋办?

To handle this case, you can mark the property with the lateinit modifier:
为了处理这种场景,你可以标记这种属性为 “lateinit”,延迟初始化登场!!

public class MyTest {
    lateinit var subject: TestSubject

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

    @Test fun test() {
        subject.method()  // dereference directly
    }
}

The modifier can only be used on var properties declared inside the body of a class (not in the primary constructor), and only when the property does not have a custom getter or setter. The type of the property must be non-null, and it must not be a primitive type.
这个修饰器只可以用在 var 属性上,而且也仅在属性没有自定义 getter 或 setter 的前提下使用。这种属性还必须是不为空的,且不是基本类型。

Accessing a lateinit property before it has been initialized throws a special exception that clearly identifies the property
being accessed and the fact that it hasn't been initialized.
访问一个延迟初始化的属性,只要它还没初始化,就会抛出一个特定的异常,这种异常会告诉你这个属性还没被初始化。(感觉这里会有坑咯)

Overriding Properties - 复写属性

See Overriding Properties

Delegated Properties - 委托属性(译为“代理属性”更好)

The most common kind of properties simply reads from (and maybe writes to) a backing field.
大多数属性都是从备用字段读取的。

On the other hand, with custom getters and setters one can implement any behaviour of a property.
但是,用自定义 getter 和 setter 可以实现任何关于属性的行为。(意思是说,可以通过重写 getter 返回某个值,而不是属性自己的值。)

Somewhere in between, there are certain common patterns of how a property may work. A few examples: lazy values, reading from a map by a given key, accessing a database, notifying listener on access, etc.
在某些地方,存在一些关于属性使用的通用模式。例如,延迟值,从 map 的 key 中取值,从数据库中取值,从监听器取值,等等。。。

Such common behaviours can be implemented as libraries using delegated properties.
这些通用行为可以通过代理属性的机制来实现的。传送门

今天就写到这了,完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,062评论 9 118
  • 概述 随着互联网的高速发展,云产业的快速突起,基础架构网络逐渐偏向基于通用计算平台或模块化计算平台的架构融合,来支...
    梅_梅阅读 53,207评论 13 70
  • 毕业后的一年,经历找工作种种碰壁,事业单位考试屡屡战败后,我终于决定放弃找工作,毅然决定自己创业。有些人创业是因为...
    yanglih阅读 571评论 0 0
  • 今天是大年三十,我的心却是五味杂陈,从今天的几件事看出,我的心智还是不够成熟和强大,来年还需继续努力修身修心。 ...
    朱砂紅塵阅读 130评论 0 1
  • 今天是你走后的第159天,我感到前未所有的失落和无助。我很想和你说话,但是我不知道去哪里找你,我希望你能看到我写的...
    你不知道的太阳花阅读 224评论 0 0