Kotlin-Anko学习(5)Kotlin语法-属性、字段、接口

本系列文章来学习 Kotlin 和 Anko 插件 通过 Kotlin 开发一个 Android 项目。

Kotlin-Anko学习(1) Kotlin、Anko 介绍
Kotlin-Anko学习(2) Kotlin 语法基础-基本类型
Kotlin-Anko学习(3) Kotlin 语法基础-关键字 package、Import、if、when、for、while、return、break、continue
Kotlin-Anko学习(4) Kotlin语法-类、继承、抽象类
Kotlin-Anko学习(5) Kotlin语法-属性、字段、接口

属性

Kotlin 中 属性的声明包括:关键字 val或var 、属性名、属性类型、初始器、访问器组成 如下:

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]
//示例
    var allByDefault:Int ?= null
    get() = if(field ==null){
       0
    }else{
      field
    }
// set访问器只有在var 可变属性中存在 val 没有set访问器
    set(value){
       field = if(value==null){
            0
       }else{
          value-2 
       } 
    }

以上示例是完整的属性声明,一般初始器(initializer)、访问器getter 和 setter 都是可选的。属性类型如果可以从初始器 、访问器中推断出来,也可以省略。总之遵循根据上下文推算出来的就可以不去特意声明。这也是kotlin 简洁性的特征之一。

  • Kotlin中,给属性直接赋值,或者调用属性的值,其实质是调用了访问器 get、set 方法,这点跟java是不同的。
  • 如果你需要改变一个访问器的可见性或者对其注解,但是不需要改变默认的实现, 你可以定义访问器而不定义其实现。

也就是在类外禁止改变 属性var 的值,可以将其声明为val 或者 自定义var set访问器的可见性 private 如下

class Kot{
var allByDefault:Int ?= null
        get() = if(field ==null){
            0
        }else{
            field
        }
      //用private进行修饰
       private set(value){
            field = if(value==null){
                0
            }else{
                value-2
            }
        }
}
//Kot.allByDefault= 1//error: 不允许设置

幕后字段

  • Kotlin 中不能直接声明字段,当属性需要字段时,会提供一个幕后字段。用field标识
  • Kotlin 中存在幕后字段的情况:
    1. 属性访问器默认实现会隐式使用幕后字段。
    2. 自定义访问器通过field显式引用幕后字段。
    var age : Int = 15
    //上面这句代码其实和下面的代码是等价的,没有区别。
    var age: Int = 15
        set(value) {
            field = value
        }
        get() {
            return field
        }

上面是kotlin对于幕后字段的使用,如何理解属性和字段呢? 比如在java中,一个person类中,在外部看来有一个getAge()和setAge(int age)方法,说明person有一个age 属性, 但是不能说person中一定有age这个字段。而在kotlin 中 可以通过关键字var/val 声明属性,但是没有关键字来声明字段:如下:

    //java
    class Person {
        private int age =15; //age 是一个字段
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }
    new Person.getAge()//获取person的age属性
  //kotlin
   class Person {
        var age: Int = 15//person 的一个age属性
        set(value) {
            field = value  // field 为一个幕后字段的标识
        }
        get() {
            return field
        }
    }

以上两段代码生成的.class文件是相同的。这就是我对 backing field 的理解。

幕后属性

kotlin 中可以通过声明一个私有的属性作为一个幕后属性

class Base{
    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")
    }
}

fun main(args: Array<String>) {
    var base = Base()
    println("table = ${base.table}" )
   
}

幕后属性的使用与java的字段相似,解决隐式幕后字段不能处理的问题,通过私有属性的get/set来优化。也是Kotlin对于空指针的一种解决方案。

编译期常量

通过 const 修饰的属性 满足如下条件:

  • 位于顶层或者是 object 的一个成员
  • 用 String 或原生类型 值初始化
  • 没有自定义 getter
//编译期常量
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"

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

延迟初始化属性与变量

通过 lateinit 修饰的属性 满足如下条件:

  • 只能用于在类体中的属性(不是在主构造函数中声明的 var 属性,并且仅当该属性没有自定义 getter 或 setter 时)
  • 用于顶层属性与局部变量。该属性或变量必须为非空类型,并且不能是原生类型
  • 在初始化前访问一个 lateinit 属性会抛出一个特定异常,明确标识该属性被访问及它没有初始化

//延迟性属性的使用如下:

public class MyTest {
    class TestSubject{
        fun method(){
            println("TestSubject.method()")
        }
    }
    lateinit var subject: TestSubject
    fun setup() {
        subject = TestSubject()
    }
   fun test() {
        subject.method()  // 直接解引用
    }
}

fun main(args: Array<String>) {
    var test = MyTest()
    test.setup()//如果不调用setup()初始化,会报如下错误
    test.test()
   
}

初始化前使用延迟性属性,会报如下错误:

Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property subject has not been initialized
    at MyTest.test(Simplest version.kt:12)
    at Simplest_versionKt.main(Simplest version.kt:18)

检测一个 lateinit var 是否已初始化 可以通过引用属性的.isInitialized来做判断:

class Foo {
    lateinit var bar: String
    fun setup() {
        println("sssss ${this::bar.isInitialized}"  )
        bar="value"
        println("xxxxx ${this::bar.isInitialized}")
    }

}

fun main(args: Array<String>) {
    Foo().setup()
}

输出结果:

sssss false
xxxxx true

属性和字段的基本使用就写完了,由于篇幅问题,再加上一个接口的学习。

接口

接口的定义与实现

Kotlin 中使用关键字 interface 来定义接口,包含抽象方法的声明、也可以有方法的实现。这就跟java有区别了,如果声明的方法没有方法体,默认是抽象方法,子类必须实现,有方法体的子类可以选择性实现。

interface MyInterface {
    //默认抽象方法
    fun bar()
    fun foo() {
      // 可选的方法体
    }
} 
class Child : MyInterface {
    override fun bar() {
        // 方法体
    }
    
}

接口中定义的属性

Kotlin 中接口可以定义抽象属性 或者提供访问器的实现,但是访问器的实现不能包含幕后字段(上面有对幕后字段的理解)。

interface MyInterface {
    val prop: Int // 抽象的

    val propertyWithImplementation: String
        get() = "foo"

    fun foo() {
        println(prop)
        println(propertyWithImplementation)
    }
}

class Child : MyInterface {
    override val prop: Int = 29
    override val propertyWithImplementation: String
        get() = super.propertyWithImplementation
}

fun main(args: Array<String>) {
    println(Child().propertyWithImplementation)
    Child().foo()
}

输出结果:

foo
29
foo

解决覆盖冲突

现多个接口时,可能会遇到同一方法继承多个实现的问题,在类的继承中已经讲解过,请参考上一篇。

参考

http://www.jianshu.com/writer#/notebooks/19396434/notes/22305825/preview
https://liyuanbiao.wordpress.com/2017/07/30/ru-he-li-jiekotlin-zhong-shu-xing/

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