对比Java学Kotlin类属性

Posted 陈蒙_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对比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")
  

以上是关于对比Java学Kotlin类属性的主要内容,如果未能解决你的问题,请参考以下文章

对比Java学Kotlin类和对象

对比Java学Kotlin密封类

对比Java学Kotlin密封类

对比Java学Kotlin密封类

对比Java学Kotlin嵌套类和内部类

对比Java学Kotlin嵌套类和内部类