深入Kotlin - 专项 - 幕后字段、幕后属性

幕后字段

Kotlin提供一个自动幕后字段,它可以通过​field​ 标志符访问。

那么幕后字段到底是什么?有什么用呢?首先来看一段代码:

class Person(val name:String){
    val nameHash get() = name.hashCode()
}

这里我们要知道 ​getter​ 和​setter​ 是什么,getter和setter是否一定与属性相关联,答案是否定的。

getter是从对象中获取特定的值,这个值完全可能是每次访问时临时计算的,也可能从其他对象那里得到的;

setter也可能是设置其他的对象;

具体我们可以看看下面的代码:

class Person(val name:String){
    val nameHash:Int = 3
        get() {
            field = 5
            return 10
    }
    fun out(){
        println(nameHash)
    }
}
fun main(args: Array<String>) {

    val ts = Person("lxx")
    println(ts.nameHash)
    ts.out()
}

运行输出,我们会看到输出两个10,看起来一切都没什么问题,别急,我们来看看它的字节码,可以反编译成java代码来看

public final class Person {
   private final int nameHash;
   @NotNull
   private final String name;

   public final int getNameHash() {
      this.nameHash = 5;        //对应上面的field = 5
      return 10;
   }

   public final void out() {
      int var1 = this.getNameHash();
      System.out.println(var1);
   }

   @NotNull
   public final String getName() {
      return this.name;
   }

   public Person(@NotNull String name) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      this.name = name;
      this.nameHash = 3;        //对应上面的val nameHash:Int = 3
   }
}

从上面可以看出:getter方法被翻译成了getNameHash()方法

我们可以看出:this.nameHash先被赋值成3,而后又被赋值成5

我们通过ts.nameHash和out()方法输出nameHash的值都是10是因为这两种方法最终都是调用getNameHash()方法,而它的返回值是10,但是实际上nameHash的值是重新赋值成了5

那么我们怎么来判断nameHash的实际值呢?看下面的代码:我们加了一个setter方法

class Person(val name: String) {
    var nameHash: Int = 3
        get() {
            field = 5
            return 10
        }
        set(value) {
            when(field){
                3 -> println("field = 3")
                5 -> println("field = 5")
                10 -> println("field = 10")
                else -> println("field = $field ,value = $value")
            }
        }

    fun out() {
        println(nameHash)
    }
}


fun main(args: Array<String>) {

    val ts = Person("lxx")
    println(ts.nameHash)
    ts.out()
    ts.nameHash = 100
}

运行输出:field = 5

说明nameHash的实际值是5,来看看字节码就更清楚了

   //只截取了setter方法
   public final void setNameHash(int value) {
      String var2;
      switch(this.nameHash) {
      case 3:
         var2 = "field = 3";
         System.out.println(var2);
         break;
      case 5:
         var2 = "field = 5";
         System.out.println(var2);
         break;
      case 10:
         var2 = "field = 10";
         System.out.println(var2);
         break;
      default:
         var2 = "field = " + this.nameHash + " ,value = " + value;
         System.out.println(var2);
      }

   }

从上面的例子可以看出 field 幕后字段的作用。不使用field,实际变量并未被初始化,但是在Kotlin中可能不会后问题,应为setter和getter最终都会被转换成对应方法调用,但是在Java调用Kotlin的时候可能就会有问题了,当然这是猜测,具体暂时就不再深究了

幕后属性

了解了幕后字段,我们再来看看幕后属性。

幕后属性是我们自己定义的,一般用于这种情况:

  • 我们希望定义一个这样的变量
  • 对外表现为只能读,不能写
  • 对内表现可以任意读写

这在Java中的应该只能通过函数来做

那么,在Kotlin中呢?Kotlin中定义变量

val只能读,不能写

var任意读写

那么同一个变量,怎么实现两种情况呢,这里就要用上我们上面看到的,getter方法实际上是被翻译成了对于变量的函数。

看代码:

class Demo{
    val size get() = _size
    private var _size:Int = 0
}

这里size是提供给对外的变量,_size是内部使用的,其实最后还是通过函数来控制的,不信可以看看字节码

public final class Demo {
   private int _size;

   public final int getSize() {
      return this._size;
   }
}

这里_size就叫做幕后属性

其实幕后属性就是我们设计类属性时的一种实现方法而已

不同于field幕后字段是Kotlin内置提供给我们的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值