Kotlin 全面学习之路 (八) -- 属性与字段

1、 声明属性

在 Kotlin 中 属性分为 可变属性只读属性

  • var 可变
  • val 只读

示例;

class Mike {
    val name: String = "Mike"
    var age: Int = 25
}

2、完整的属性声明

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

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

其中 初始器(initializer)gettersetter 都是可选的。

我们可以自定义访问器,具体场景为新建一个可以判断是否为正方形的矩形类:

class Rectangle(val height: Int, val width: Int) {
    val isSquare: Boolean
        get() {    // 属性的获取函数声明
            return height == width
        }
}

一个自定义 setter 的例子;

var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // 解析字符串并赋值给其他属性
}

如果要 改变一个访问器的可见性或者对其注解,那么不需要改变其默认实现,只需要定义该属性的访问器而不是其实现,以下为例:

var setterVisibility: String = "abc"
private set // 此 setter 是私有的并且有默认实现
var setterWithAnnotation: Any? = null
@Inject set // 用 Inject 注解此 setter

3、Backing Field

自己对 Kotlin 中相关 Backing Field 的内容比较疑惑,单独写一篇博客来记录,具体详见Kotlin 全面学习之路 (九) – 对 Kotlin 中 Backing Field 的理解

4、后端属性(Backing Property)

如果你希望实现的功能无法通过这种 “隐含的后端域变量” 方案来解决, 你可以使用 后端属性(backing property) 作为替代方案:

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 中完全相同, 因为后端私有属性的取值方法与设值方法都使用默认实现, 我们对这个属性的访问将被编译器优化, 变为直接读写后端域变量, 因此不会发生不必要的函数调用, 导致性能损失.

5、编译期常量

在阐述 编译期常量前,我们需要先理解 运行时常量编译期常量

  • 运行时常量是编译时并不知道其值,真正运行的时候才获取
  • 编译期常量是编译时候就知道其值的常量
  • kotlin中 val 并不是编译期常量,可通过反射的方式修改值,要将其转成编译期常量需要加上 const 关键词,可提高运行效率。

已知值的属性可以使用 const 修饰符标记为 编译期常量,不过这些属性需要满足以下要求:

  • 位于顶层或者是 object 的一个成员。验证这一条件十分容易,在一个类的属性前添加 const 修饰符,此时你会看到错误信息:const 'val' are only allowed on top level or in object
  • 用 String 或原生类型初始化 。同样可以在代码中进行验证 。
  • 没有自定义 getter 。同样可以在代码中进行验证 。

这些属性可以用在注解中:

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is depr
ecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { …… }

6、延迟初始化属性与变量

一般的,属性声明为 非空类型 必须在构造函数中 初始化,但是有些情况下,我们不能在构造函数内提供一个非空初始化属性,比如需要一个类对象,但是此类对象需要通过一系列操作才可以得到,同时你还想在类体中引用该对象是避免空检查,那么此时延迟初始化属性就是必要的了。

为了实现延迟初始化属性,我们可以使用 lateinit 修饰符来标记该对象(属性)。

public class People{
  lateinit var dog: Dog()

  fun setDog(dog: Dog){
    this.dog = dog
  }

  fun showDogInfo(){

    //dog?.show()
    //dog!!.show()
    // 此时就不用做空检查,以上两行代码就没有必要了
    dog.show()
  }  
}

在初始化前访问一个 lateinit 属性会抛出一个特定异常,该异常明确标识该属
性被访问及它没有初始化的事实。

7、覆盖属性

子类覆盖父类的相应属性使用 override 修饰符修饰该属性。

  • var 属性可以覆盖 val 属性,反之不行
  • 每个声明的属性可以由具有初始化器的属性或者具有 getter 方法的属性覆盖。

8、委托属性

自己对 Kotlin 中相关委托的内容比较疑惑,单独写一篇博客来记录,具体详见


参考资料

kotlin in action in Chinese

Kotlin学习笔记(十八)常量与变量

猜你喜欢

转载自blog.csdn.net/Strange_Monkey/article/details/82628395
今日推荐