Kotlin 笔记 3.2:属性和字段

属性

对于 Kotlin 中的属性,有两种方式声明:

  • var:声明一个可变属性
  • val:声明一个只读属性

二者区别没啥好说的,var 声明属性后期可以赋值,而 val 声明的属性,初始化赋值之后,不能再赋值了,类似于 Java 中的 final 属性。

在 Kotlin 中,var 类型的属性是自带 getter 和 setter 方法,val 自带 getter 方法(因为它后期不能修改嘛),我们直接通过对象调用该属性,实际上是调用了它的 getter 或 setter 方法:

fun main() {
    var person = Person("张三", 20)
    person.age = 10 //实际调用了 age 属性的 setter 方法
    person.name = "李四"  //这句会报错,因为 name 属性是 val 类型的
}

class Person(name: String, age: Int) {
    val name: String = name
    var age: Int = age
}

当然,我们也可以自定义他们的 setter 和 getter 方法:

fun main() {
    var person = Person("张三", 20)
    person.name = "李四"
    person.age = 20
    print(
        "${person.name}      ${person.age}"
    )
}

class Person(name: String, age: Int) {
    var name: String = name
        get() {
            return "$field$field"
        }
    var age: Int = age
        set(value) {
            if (value > 18) {
                field = 18
            } else {
                field = value
            }
        }
}
//输出结果是:李四李四      18

幕后字段

在文档中,关于幕后字段的解释有点儿说的云里雾里,我是这么理解的:

首先咱们写 Java 时经常这么写:

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

先声明一个字段 name,然后可以通过它的 setter 和 getter 方法来操作它,但是如果在 Kotlin 中也按照这个思路来写它的 setter 和 getter 方法,就会有问题:

fun main() {
    var person = Person("张三")
    person.name = "李四"
    print(
        "${person.name} "
    )
}

class Person(name: String) {
    var name: String = name
        set(value) {
            name = "$value$value"
        }
}

在 Kotlin 中,提供了一个 field,我们 get 和 set 方法的操作,实际上是对这个 field 的操作,我们这样修改一下就没问题了:

class Person(name: String) {
    var name: String = name
        set(value) {
            field = "$value$value"
        }
}

为什么需要这么做呢,我刚开是也是觉得蛋疼,查了一圈,各种文章也是抄官方文档,照样云里雾里,最后看到一个靠谱的通俗易懂的解释:

当我们在外部使用 对象.属性 来操作属性的时候,实际上,是调用了属性的 set 或者 get 方法,那再看之前错误的写法:person.name = "李四" 翻译过来就是 person.setName,而在 set 方法内部,再调用 name = "$value$value",岂不是在 set 方法中调用 set 方法,无出口递归嘛。而 field 就相当于给你提供了一个隐藏的私有变量。