IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> kotlin基础7:getter setter 幕后字段field -> 正文阅读

[移动开发]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<String>) {
   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, "<set-?>");
      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<String>) {
   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, "<set-?>");
      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<String>) {
   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<String>) {
   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<String>) {
   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删掉,则编译通不过,并且这种情况下不可使用幕后字段

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-01-01 14:01:50  更:2022-01-01 14:06:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:31:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码