之前我们讲到了类,怎么创建类,实例化类,类构造函数拥有两种,一种是主要构造函数,另一种是次要构造函数,次构造函数为了方便管理都需要调用一下主构造函数;
那么这一期我们来讲成员变量的延时;我们用到的一个关键字叫lateinit,它是用来延时实例化一个成员变量的时间用的,一般会延时到使用时实例化,当然还有一种是饿汉式:by lazy的组合使用来实现自动化的延时成员变量的实例化时;而且对于kotlin来说不想Java的代码对顺序那么灵活,一旦代码位置错了,就可能出现空指针:
1、加载类时的执行顺序
当我们调用次构造函数使用类时,类里面的成员、构造函数和init代码块是怎样的一个执行顺序呢?
我们知道,执行次构造函数必然需要调用主构造函数,而主构造函数有需要将成员全都初始化好,所以会先执行一气呵成的参数,就是带有val等参数类型的参数,然后执行init和其他的参数初始化,注意参数和init之间的先后顺序看你先写哪一个就限制性,所以我们可以说这些成员参数的执行和init代码块是并行执行的:
- public Tony2(@NotNull String _name, int age, @NotNull String info) {
- Intrinsics.checkNotNullParameter(_name, "_name");
- Intrinsics.checkNotNullParameter(info, "info");
- super();
- this.age = age;
- this.info = info;
- this.name = _name;
- String var4 = "我是init代码块:" + _name;
- System.out.println(var4);
- this.age1 = this.age;
- }
从反编译的Java代码可以看到,这个类调用次构造初始化时限制性了主构造,然后在里面执行了一气呵成的参数age和info,再就是先写的init代码块,最后是最后写的age,最最后执行次构造函数;
//3.1 设置类构造函数的默认值 /* * 因为有了默认值,于是我们可以不传参数,但是不传参是调用的是次构造还是 * 主构造呢?答案是主构造优先 * */ Tony3() //3.2 初始化代码块 Tony2("Tony",4,"在学习kotlin") Tony3("",4,22)
2、延时初始化成员,有时候我们想要我们的成员变量只有当用到的时候进行初始化,这里就可以使用延时成员变量的操作,含有两种,一种手动的延时,一种是自动的延时,也叫作懒汉式初始化变量:
- //4.1 声明一个延时初始化的成员,不需要赋值
- lateinit var info: String
- //定义一个函数来对成员赋值
- fun getrequest(){
- info = "Tony"
- }
- //打印结果
- fun showresult(){
- //注意这个isInitialized函数只能用在手动的演示初始化里面
- if(::info.isInitialized){
- println("初始化成功:${info}")
- }else{
- println("未被初始化")
- }
- }
主要步骤就是先定义好一个成员变量,注意要是用lateinit关键字表示是延时变量,然后定义一个函数用来给成员赋值,最后是使用这个变量;
第二种自动延时初始化是使用了by lazy:
- //4.2 还可以是by lazy的方式来自动延时初始化
- val info2: String by lazy {
- getrequest2()
- }
- fun getrequest2(): String{
- return "jerry"
- }
- fun showresult2(){
- println("自动延时初始化:${info2}")
- }
我们在lazy的括号里调用这个赋值函数即可:
- //3.4 自动懒加载(by lazy)和懒加载(手动)
- Tony3().getrequest()
- Tony3().showresult()
- //4.3 使用自动延时初始化,我们就不需要手动调用函数,我们用到时它自己会调用
- Tony3().showresult2()
3、类的陷阱
以为kotlin是严格按照代码生成顺序执行的,所以大家务必对顺序需要了解:
- //5.1 在类加载里面的陷阱
- /*
- * 当然,说是陷阱,其实只是对于我们学习了Java的人来说容易犯的错,就是Java对
- * 代码的生成顺序是很灵活的,一般不会有顺序问题,但是kotlin不一样,它严格要求
- * 代码的顺序,如果没有按照顺序写代码,可能非常容易会有空指针异常的问题
- *
- * */
- Tony4("Tony").getinfo()
有时候我们可能会写一个成员是一个函数的返回值,而这个函数的返回值又是一个成员变量的值,不过这个变量是在调用函数的成员变量之后,那么我们会拿到一个null值。因为在他没有实例化的时候调用了它:
- class Tony4(_info: String){
- //5.2 试一试不按照顺序的效果
- fun getinfo(){
- println("你传入的字符串长度:"+info.length)
- println("乘法结果:${number}")
- println("取值结果:${info1}")
- }
- //结果我们拿到的info1是一个null,这是因为顺序,info在info1的后面生成
- var info1 = getmethods()
- val info = _info
- //所以当我们调用获取info的值的函数时,info还没有赋值,所以为null
- fun getmethods() = "我拿到了${info}"
- var number = 9
- init {
- number = number.times(9)
- }
- }
总之,kotlin当中的代码时严格按照代码顺序来执行,所以我们需要格外注意;那么这一期就到这。