属性和字段

在java中 有类成员变量的定义,而且类成员变量可以只声明不初始化(因为在构造函数中java会给没有初始化的成员变量,赋予默认值)

总述:
在面向对象语言中,一个类是有属性和行为的,在kotlin中与之对应的是属性和方法,这里我们详细讨论下属性这个东西.属性是一个类的某个特征,这个特征可能是可变的如年龄,也可能是不可变的如性别,我们在使用某个类的某个属性时,我们不关心这个属性时如果实现存储的,我们关心的是 得到这个属性的值,或者改变这个属性的值,

在java中是用成员变量或者说是字段来表明属性的,这个字段是存储在栈中,但是在kotlin中,kotlin彻底封装了属性的获取和设置,kotlin会为每一个属性都默认提供get和set方法(val只提供get方法),我们获取某个属性时,实际上就是调用这个get方法,设置属性时就是调用set方法.属性既然是通过get方法获取的,那属性可能仅仅是存在于get方法中,而不会存储在栈中的某个变量中,比如我们设置一个val类型 num属性,我们获取该属性的时候是通过其get方法即是: public final int getNum() { return 3; } 这个方法是直接返回3,所以num属性并没有存储在某个变量中,仅仅是存储在kotlin的代码中,所以得到一个结论:在kotlin中类是有属性的,但是一个属性不一定对应着一个字段.

下面我们详细的看下kotlin的属性:

Person.Class

val age = 1  //不可变整形
var name = "张三" //可变String类型
注:这里 age name都是属性

这里我们是声明了2个属性,一个是int型的age,一个是Stirng类型的name,而且age是不可变类型,name是可变类型的,这里我们将上述2行代码反编译成java语言

private final int age = 1;
 @NotNull
 private String name = "张三";
public final int getAge() {
  return this.age;
}
@NotNull
public final String getName() {
  return this.name;
}
public final void setName(@NotNull String var1) {
  Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
  this.name = var1;
}

这里我们看到,kotlin声明了2个字段,其中kotlin将val类型的int 声明为私有的不可变的类型(这也就是常说的val只读,不可改变),而且自动提供了一个get方法.对于var类型的String 则声明为私有的没有被final修饰,并且同时提供了get set方法

在kotlin中通过对象调用属性时,实际上是调用get方法,赋值是调用set方法

int a = person.age;
这语句反编译为java为
int a = person.getAge();
person.name = "lisi";
这句语句被反编译为
person.setName("lisi");

前面我们说到了kotlin中一个属性不一定代表着一个字段,什么意思呢(而前面我们反编译属性的声明是有字段的呀):看下面代码

var attention: String
    get() {
        return "ssss"
    }
    set(value) {
    }
val num: Int
   get() = 3

上面我们声明了2个属性一个 var可变的String类型的attention 另一个是val不可变的int类型的num,我们下面再看下其反编译成java代码的结果

@NotNull
 public final String getAttention() {
  return "ssss";
  }

 public final void setAttention(@NotNull String value) {
  Intrinsics.checkParameterIsNotNull(value, "value");
  }

   public final int getNum() {
      return 3;
  }

我们注意到,反编译成java代码后,是没有成员变量的(字段),我们接下来分析下为什么会这样,

属性是一个类的特征,对于这个特征我们需要获取或者改变,在java中字段是属性的实现方式,而并不是说属性就是字段,在kotlin中属性是用get和set方法实现的,所以在这个过程中,我们可以不用字段,直接使用get返回一个值用作属性.但是在kotlin中真的没有类似于java中字段这种东西吗,答案是有的,只是在kotlin中叫做幕后字段,它是用来完善get set方法以实现属性的功能.

在kotlin中,当get和set方法需要使用字段来实现属性的功能时,编辑器会自动生成一个字段,该字段叫做幕后字段,并且该幕后字段可以在get和set方法使用field标识符来引用,比如

var age = 2
    get() {
        return if (field >=0) {
            field
        } else {
            -field
        }
    }
	 set(value) {
        field = 2 * value
    }

上述代码中field就是存储的幕后字段,所以我们得到的属性age的值永远都是大于0的, 在set方法中我们看到幕后字段存储的值是设置的值的2倍,这里属性和字段相同吗,完全不同,所以说
幕后字段是实现属性的一种手段,幕后字段不等于属性,幕后字段和get set方法共同组成属性

我们知道幕后字段是kotlin编辑器自动为我们生成的,那生成的条件是什么呢:官方说明是:
如果属性至少一个访问器使用默认实现,或者自定义访问器通过 field 引用幕后字段,将会为该属性生成一个幕后字段。