属性和域

属性和域

什么是属性?
实际是指getter和setter方法,虽然和字段是两个概念,姑且可以理解为有getter和setter方法的字段。
如何声明?
完整语法:

var 属性名: <属性类型> [= <初始化器>]
    [<getter>]
    [<setter>]

其中的初始化器、getter和setter方法是可选的。如果没有自定义getter和setter的实现,就会使用默认的。
以下两种情况属性类型也可省略:

  1. 可以通过初始化器推断出来
  2. 被声明的属性会覆盖基类中的属性,并且通过基类中属性的类型推动出来

两种属性:
只读属性:val关键字声明
可变属性:var关键字声明
访问属性:
访问属性使用点操作符,对象名.属性名,如:

var person = Person()
person.name = "张三"

和oc一样,点操作符内部的实现也是调用getter或setter访问器。并且在Kotlin中访问Java的属性时,仍然可以用点语法访问Java对象中的属性。

如果要修改访问器的可见性或添加注解,但又不需要修改默认实现,你可以重新定义方法,但不定义它的实现,这样仍然是使用默认实现。如:

var setterVisibility: String = "abc"
    private set // 设值方法的可见度为 private, 并使用默认实现

var setterWithAnnotation: Any? = null
    @Inject set // 对设值方法添加 Inject 注解

属性的后端域变量(Backing Field)

Kotlin类不能拥有域变量(也就是Java中的成员变量),但是使用访问器时又需要这种域变量,所以Kotlin提供了后端域变量,可以用field标识符来访问。
如:

var counter = 0 // 初始化给定的值将直接写入后端域变量中
    set(value) {
        if (value >= 0) field = value
    }
  }

field就代表编译器自动生成的后端域变量,并且只允许在属性访问器中使用

什么情况下会生成后端域变量?

  1. 访问器中任一个使用默认实现
  2. 自定义的访问器中通过field关键字访问属性

下面这种情况下就不会生成后端域变量:

val isEmpty: Boolean
    get() = this.size() == 0 //自定义访问器没有使用field关键字访问属性

为什么不用this关键字访问属性呢?
试想下,this代表当前对象,而上面说过通过点操作符,访问属性时,实际上是调用了访问器,在访问器内部实现中再调用访问器就会出现无限递归。所以,通过Backing Filed可以避免这个问题。

后端属性(Backing Property)

如果你的操作是想访问属性内部的属性,或者集合里面的元素,而不是属性本身,那么你可以声明这个属性为private的(后端属性),然后定义个public的属性并自定义其访问器,在自定义的访问器内部访问后端属性。如:

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        // 这里的操作不希望属于 backing filed,
        if (_table == null) {
            _table = HashMap() 
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }

其实也没有什么神秘的,其实就像Java中我们通常写的,在访问私有属性时,在自定义访问器中添加些限制,避免取到或设置非法的值一个道理。只是在Kotlin中取了个高大上的名字。

编译器常数值

值在编译期就能确定的属性,用const关键字修饰。满足以下条件的属性可以标记为编译器常数值:

  1. 必须是顶级属性或是object的成员
  2. 值必须是String或基本类型
  3. 不能自定义取值方法

如:

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

编译器常数值可以用在注解内:

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

延迟初始化属性

通常属性被声明为非null类型就必须就地初始化。但是,这种限制在很多情况下是不方便的,比如,声明的属性通过依赖注入的方式来初始化。这种情况下,你就可以使用lateinit关键字来修饰声明的属性。
如:

public class MyTest {
    lateinit var subject: TestSubject

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

    @Test fun test() {
        subject.method()  // 直接访问属性
    }
}

使用限制:

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

推荐阅读更多精彩内容