【对比Java学Kotlin】类属性

声明属性

Kotlin 类里面的属性,如果是可变则用 var 修饰,如果是只读类型,则用 val 修饰。比如:

class Address {
    var name: String = "Holmes Sherlock"
    var streed: String = "Baker"
    var city: String = "London"
    var state: String? = null
    var zip: String = ""
}

使用属性的时候,直接引用名字即可:

fun copyAddress(address: Address): Address {
    val result = Address() // 与 Java 不同,新建示例无需 new 关键字 
    result.name = address.name
    result.street = address.street
    // ...
    return result
}

Getter&Setter

在 Kotlin 中,声明一个属性的完整语法是:

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

当然,初始化和 Getter 和 Setter 是可以省略的。如果属性类型可以根据上下文推断出来,也是可以省略的。比如:

var allByDefault: Int? // 编译报错,必须初始化,使用了默认 getter 和 setter 方法
var initialized = 1 // 编译 ok,有初始化,使用了默认 getter 和 setter 方法

val 类型的数据声明与 var 类型稍稍不同:

val simple: Int? // Int 类型, 默认 getter,必须在构造函数里面进行初始化
val inferredType = 1 // Int 类型,默认 getter

所以,跟 Java 不同,Kotlin 里面的属性都是默认有 Getter 和 Setter 方法的,其中 val 修饰的只读属性只有 Getter 没有 Setter。
这是 Kotlin 比 Java 简洁的一个特点,想象每次写 Java 的数据类比如 XXBean 时,是写 getter&setter 还是直接引用属性?都要纠结一会儿,现在好了,Kotlin 直接内置了。
而且,这个特点也帮助 Kotlin 实现了声明式编程。初次接触 Kotlin 时,总是很好奇如下代码是如何实现的:

aTextView.visibility = View.VISIBLE
aTextView.text = "赋值之后立即生效的文本"

感情赋值之后应该是调用了 setter 方法来实现的。
除此之外,内置的 setter 方法也为我们提供了一个 Java 无法实现的能力:监控所有的属性赋值操作。因为 Kotlin 属性的所有赋值操作都会调用对应的 setter 方法,我们就可以重写 setter 或者通过代理来实现监听。
如何重写 getter 和 setter 方法呢?比如:

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

这样,当我们每次使用 isEmpty 属性时都会调用这个自定义的 getter。
同样的,我们也可以自定义 setter:

var stringReprentation: String
get() = this.toString
set(value) {
    setDataFromString(value) // 内部转换逻辑
}

注意,set() 的参数 value 只是一个约定俗称,你完全可以换成其他你喜欢的变量名。
如果我们需要更改 setter 的访问权限而且不需要更改 setter 方法体,我们可以这样:

val setterVisibility: String = "abc"
  private set

val setterWithAnnotation: String = "abc"
  @Inject set

看下面这段代码:

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

执行后你会发现,这段代码会导致堆栈溢出,因为 setter 方法里面的赋值会继续调用 setter 直至堆栈溢出。该怎么解决呢?这就要说到 Backing field 了。

Backing Field

为了解决上面的问题,默认的 setter 方法应该这么写:

var stringReprentation: String
get() = this.toString
set(value) {
    field = value
}

这个 field 的作用域在其绑定的属性的 setter 方法内,可以看成是属性在内存的备份。我们可以将 Backing Field 翻译成幕后字段或影子字段。

Backing 属性

我们也可以声明自己的 Backing 性质的属性,比如下面的 _table 变量:

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")
  }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值