• kotlin基础教程:<8>类的延时成员变量、初始化代码块和陷阱


    之前我们讲到了类,怎么创建类,实例化类,类构造函数拥有两种,一种是主要构造函数,另一种是次要构造函数,次构造函数为了方便管理都需要调用一下主构造函数;

    那么这一期我们来讲成员变量的延时;我们用到的一个关键字叫lateinit,它是用来延时实例化一个成员变量的时间用的,一般会延时到使用时实例化,当然还有一种是饿汉式:by lazy的组合使用来实现自动化的延时成员变量的实例化时;而且对于kotlin来说不想Java的代码对顺序那么灵活,一旦代码位置错了,就可能出现空指针:

    1、加载类时的执行顺序

    当我们调用次构造函数使用类时,类里面的成员、构造函数和init代码块是怎样的一个执行顺序呢?

    我们知道,执行次构造函数必然需要调用主构造函数,而主构造函数有需要将成员全都初始化好,所以会先执行一气呵成的参数,就是带有val等参数类型的参数,然后执行init和其他的参数初始化,注意参数和init之间的先后顺序看你先写哪一个就限制性,所以我们可以说这些成员参数的执行和init代码块是并行执行的:

    1. public Tony2(@NotNull String _name, int age, @NotNull String info) {
    2.      Intrinsics.checkNotNullParameter(_name, "_name");
    3.      Intrinsics.checkNotNullParameter(info, "info");
    4.      super();
    5.      this.age = age;
    6.      this.info = info;
    7.      this.name = _name;
    8.      String var4 = "我是init代码块:" + _name;
    9.      System.out.println(var4);
    10.      this.age1 = this.age;
    11.   }

    从反编译的Java代码可以看到,这个类调用次构造初始化时限制性了主构造,然后在里面执行了一气呵成的参数age和info,再就是先写的init代码块,最后是最后写的age,最最后执行次构造函数;

     //3.1 设置类构造函数的默认值    /*    * 因为有了默认值,于是我们可以不传参数,但是不传参是调用的是次构造还是    * 主构造呢?答案是主构造优先    * */    Tony3()    //3.2 初始化代码块    Tony2("Tony",4,"在学习kotlin")    Tony3("",4,22)

    2、延时初始化成员,有时候我们想要我们的成员变量只有当用到的时候进行初始化,这里就可以使用延时成员变量的操作,含有两种,一种手动的延时,一种是自动的延时,也叫作懒汉式初始化变量:

    1. //4.1 声明一个延时初始化的成员,不需要赋值
    2.    lateinit var info: String
    3.    //定义一个函数来对成员赋值
    4.    fun getrequest(){
    5.        info = "Tony"
    6.   }
    7.    //打印结果
    8.    fun showresult(){
    9.        //注意这个isInitialized函数只能用在手动的演示初始化里面
    10.        if(::info.isInitialized){
    11.            println("初始化成功:${info}")
    12.       }else{
    13.            println("未被初始化")
    14.       }
    15.   }

    主要步骤就是先定义好一个成员变量,注意要是用lateinit关键字表示是延时变量,然后定义一个函数用来给成员赋值,最后是使用这个变量;

    第二种自动延时初始化是使用了by lazy:

    1. //4.2 还可以是by lazy的方式来自动延时初始化
    2.    val info2: String by  lazy {
    3.        getrequest2()
    4.   }
    5.    fun getrequest2(): String{
    6.        return "jerry"
    7.   }
    8.    fun showresult2(){
    9.        println("自动延时初始化:${info2}")
    10.   }

    我们在lazy的括号里调用这个赋值函数即可:

    1.    //3.4 自动懒加载(by lazy)和懒加载(手动)
    2.    Tony3().getrequest()
    3.    Tony3().showresult()
    4.    //4.3 使用自动延时初始化,我们就不需要手动调用函数,我们用到时它自己会调用
    5.    Tony3().showresult2()

    3、类的陷阱

    以为kotlin是严格按照代码生成顺序执行的,所以大家务必对顺序需要了解:

    1. //5.1 在类加载里面的陷阱
    2.    /*
    3.    * 当然,说是陷阱,其实只是对于我们学习了Java的人来说容易犯的错,就是Java对
    4.    * 代码的生成顺序是很灵活的,一般不会有顺序问题,但是kotlin不一样,它严格要求
    5.    * 代码的顺序,如果没有按照顺序写代码,可能非常容易会有空指针异常的问题
    6.    *
    7.    * */
    8.    Tony4("Tony").getinfo()

    有时候我们可能会写一个成员是一个函数的返回值,而这个函数的返回值又是一个成员变量的值,不过这个变量是在调用函数的成员变量之后,那么我们会拿到一个null值。因为在他没有实例化的时候调用了它:

    1. class Tony4(_info: String){
    2.    //5.2 试一试不按照顺序的效果
    3.    fun getinfo(){
    4.        println("你传入的字符串长度:"+info.length)
    5.        println("乘法结果:${number}")
    6.        println("取值结果:${info1}")
    7.   }
    8.    //结果我们拿到的info1是一个null,这是因为顺序,info在info1的后面生成
    9.    var info1 = getmethods()
    10.    val info = _info
    11.    //所以当我们调用获取info的值的函数时,info还没有赋值,所以为null
    12.    fun getmethods() = "我拿到了${info}"
    13.    var number = 9
    14.    init {
    15.        number = number.times(9)
    16.   }
    17. }

    总之,kotlin当中的代码时严格按照代码顺序来执行,所以我们需要格外注意;那么这一期就到这。

  • 相关阅读:
    Sulley入门教学——简介、安装(Win7、VMware)
    RocketMQ实战之在线停机和扩容
    【微服务】Nacos的优势
    设计模式-开闭原则和迪米特法则
    单文件组件环境配置步骤---vue-cli版
    unity基础3-数据持久化
    前端Vue返回顶部和底部四个角[样式加方法](代源码和详图)
    为什么要写测试用例,测试用例写给谁看?
    回调函数和钩子函数
    基于ESP32-WROOM-32的USB转WIFI模块设计
  • 原文地址:https://blog.csdn.net/aiwanchengxu/article/details/125492007