kotlin基础7:getter setter 幕后字段field

kotlin基础7:getter setter 幕后字段field,第1张

kotlin基础7:getter setter 幕后字段field 一、对比java的类声明和kotlin的类声明
public class Person {
    private String name;

    public String getName() {
        return name;
    }

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

熟悉java的一定很清楚,这是最常见的写法

然而在kotlin里面,写法就非常简单了:

class Person {
   lateinit var name:String
}

fun main(args: Array) {
   var person = Person()
    person.name = "hello"
    kotlin.io.println("name: ${person.name}")
}
lateinit是让name保持和java的写法一样,不必在声明时就赋值

一般情况下,不用像java那样实现setter和getter方法,编译器已默认实现这两个方法了,在Android studio双击shift,输入Show Kotlin Bytecode,然后点击Decompile,便可查看kotlin文件对应的java代码,上面kotlin代码的person类经过编译器后的java代码是这样的:

public final class Person {
   public String name;

   @NotNull
   public final String getName() {
      String var10000 = this.name;
      if (var10000 == null) {
         Intrinsics.throwUninitializedPropertyAccessException("name");
      }

      return var10000;
   }

   public final void setName(@NotNull String var1) {
      Intrinsics.checkNotNullParameter(var1, "");
      this.name = var1;
   }
}

虽然多了很多东西,但是可以看到默认实现了getter和setter

如果我想在get和set方法里面做一些 *** 作呢,如果是java,在get或set方法里面做一些判断,取出或者设置不同的值是很常见的做法,如果是kolin,则需要可借助幕后字段field了。

首先是自定义gettter:

class Person {

    var name:String = ""
    get() {
        kotlin.io.println("访问了get")
        return name +"666"
    }

}
fun main(args: Array) {
   var person = Person()
    person.name = "hello"
    kotlin.io.println("name: ${person.name}")
}

运行结果:

 可以看到访问了很多次get(),并且还出现了StackOverflowError异常,如果习惯了java会以为以上写法没什么问题,我们再次反编译kotlin代码看看:

public final class Person {
   @NotNull
   private String name = "";

   @NotNull
   public final String getName() {
      String var1 = "访问了get";
      System.out.println(var1);
      return this.getName() + "666";    //这里又访问了getName(),循环调用了
   }

   public final void setName(@NotNull String var1) {
      Intrinsics.checkNotNullParameter(var1, "");
      this.name = var1;
   }
}

可以看到,getName()里面又调用了getName(),并且没有退出时机,一直递归调用最后导致了StackOverflowError。

二、幕后字段field

以下是自定义getter和setter的各种情形,想要改变值,必须借助幕后字段field

情况一:属性是String等引用类型,即使getr和sett都不使用默认的实现方法,也可以使用幕后字段field(笔者曾在各种地方看到过,说如果get和set都不使用编译器默认提供的,则没有幕后字段,但是经过笔者验证,这种说法并不正确),这种情况下需要赋初始值,并且不可使用lateinit

class Person {

    var name:String = ""      //需要赋初始值,并且不可使用lateinit!!!
    get() {
        kotlin.io.println("访问了get")
        return field +"666"
    }
    set(value) {
        kotlin.io.println("访问了set")
        field = value
    }

}

fun main(args: Array) {
   var person = Person()
    person.name = "hello"
    kotlin.io.println("name: ${person.name}")
}

运行结果:

 可以看到get()和set()对属性都起到了修改的作用

情况二:属性是Int,boolen等基础属性,不给予初始赋值,并且get和set都使用了自定义的方法,这种情况下不可使用幕后字段

class Person {
    var age:Int
    get() {
        kotlin.io.println("访问了get")
        return  2
    }
    set(value) {
        kotlin.io.println("访问了set")
        value + 1
    }

}

fun main(args: Array) {
   var person = Person()
    person.age = 9
    kotlin.io.println("name: ${person.age}")
}

运行结果:

 但是这种写法,无论set里面做了什么 *** 作,最终取到的值都是get返回的值,并且这种情况下反编译为java代码,会看不到age属性了,以下是反编译后的java代码:

public final class Person {
   public final int getAge() {
      String var1 = "访问了get";
      System.out.println(var1);
      return 2;
   }

   public final void setAge(int value) {
      String var2 = "访问了set";
      System.out.println(var2);
      int var10000 = value + 1;
   }
}

情况三:属性是Int,boolen等基础属性,给予初始赋值,并且get和set都使用了自定义的方法,这种情况下可以使用幕后字段

class Person {
    var age:Int = 0
    get() {
        kotlin.io.println("访问了get")
        return  field + 2
    }
    set(value) {
        kotlin.io.println("访问了set")
        field = value + 1
    }
}

fun main(args: Array) {
   var person = Person()
    person.age = 9
    kotlin.io.println("age: ${person.age}")
}

运行结果:

由上面的例子可以得出以下结论:

1.只用通过幕后字段field去 *** 作属性值才能改变属性的值,自定义的get和set可同时作用于属性,get和set不是必须都要自定义,可以根据实际情况去自定义哪一个方法

2.如果需要使用幕后字段,则需要在声明时赋予初始值

3.对于String等非基础数据类型,不管是自定义了get或者set,则必须赋予初始值

4.只要自定义了get或者set。无论是那种数据类型,都不能使用lateinit

5.如果是基础数据类型,如果不赋予初始值又不使用lateinit,则必须同时自定义get和set,如情况二,如果把自定义的get或者set删掉,则编译通不过,并且这种情况下不可使用幕后字段

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5686210.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存