深入理解Kotlin无参构造函数

Unsafe 创建实例

在java中 创建一个对象 其实主要就是3种方法

  1. 通过new 关键字来创建 这种是最常见的

    1. 通过反射构造方法来创建对象 这种也不少见。很多框架中都有使用。

    1. Unsafe类来创建实例 ,这种情况非常少见。

    这里先讲讲Unsafe创建实例的方法。

    我们首先创建一个Pserson类

    public class Person {
        public Person() {
            System.out.println("Person cons");
        }
    }
    

    再创建一个User类

    public class User extends Person {
        public String getName() {
            return name;
        }
    
    
        public void setName(String name) {
            this.name = name;
        }
    
    
        private String name;
    
    
        public User() {
            System.out.println("User cons");
        }
    }
    
    
    

    注意看 这2个类的特点是 都有无参的构造方法

    然后我们看看 怎么通过Unsafe 来操作 生成一个User的对象

     try {
                Class klass = Unsafe.class;
                Field field = null;
                field = klass.getDeclaredField("theUnsafe");
                field.setAccessible(true);
                Unsafe unsafe = (Unsafe) field.get(null);
                User user=(User) unsafe.allocateInstance(User.class);
                System.out.println(user.getName());
                user.setName("wuyue");
                System.out.println(user.getName());
    
    
                System.out.println("分割线");
                User user1=new User();
    
    
    
    
            } catch (NoSuchFieldException | IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }
    
    
    

    然后我们看下结果:

    可以看出来 通过Unsafe构造出来的对象 在使用上和我们用new 关键字 构造出来的对象是一样的。但是有个很大的不同是:UnSafe 创建出来的对象 是没有使用构造方法的,也就是说 构造函数没有走。

    大家可以看下日志:

    分割线之前 我们用Unsafe的方法创建出来的对象 是没有构造函数的日志的。(很多java boy 看到这里是不是不敢相信?其实Unsafe 这个类下面很多方法 都是这么没有节操。)

    这里我们不深究背后的原理(其实是我不知道),大家只要记住这个结论即可

    kotlin的非空与不非空

    看下这2个函数:

    fun one(msg:String){
    
    
    }
    
    
    fun two(msg:String?){
    
    
    }
    
    
    

    唯一的区别就是函数参数 这里 一个是可空的 一个是不可空的。我们反编译看一下:

    所以你看 这里还是挺危险的。如果你这个 one 的函数 是给java的人调用,而他恰好又传了一个null 进来 那么走到one函数里面 就会抛异常了。

    例如我们在java代码里面 调用这个one函数 参数写成 第一个小节里的 user.getName()  那就必然会报错抛异常了

    kotlin 的 构造函数

    这里实际上是非常坑爹的一点。大家都知道 纯java的代码  如果你不写任何构造函数,编译器也会帮你生成一个无参的构造函数。

    但是在kotlin中,这个特性被抹掉了

    举例说明:

    我们首先定义一个kotlin的普通类

    class KUser(var name:String)
    

    然后在java的代码中 调用他 试试看

    你会发现这样是行不通的, 这种定义类的方式,不会帮你生成默认的无参构造函数。

    我们再试试data class 看看行不行

    data class KUser2(var name: String)
    

    依旧也是不行的,看来 kotlin中 不管是class 还是data class 都不会自动帮你生成 无参的构造函数。

    有没有方法可以避免这种情况。当然是可以的。

    比如我们把定义的属性 手动给他指定一个默认值

    class KUser(var name:String="")
    
    
    data class KUser2(var name: String="")
    

    或者手动指定一个无参的构造函数

    data class KUser2(var name:String ,var age:Int){
        constructor():this("123",0)
    }
    

    总之只要你指定了全部属性 都有默认值 ,那么就肯定会有无参的构造函数的

    当kotlin 碰上序列化

    有了上面的基础知识,我们再来看下面的 例子就清晰的多了。

    首先看下kotlin官网中的 原文:

    翻译成人话就是:kotlin 要想和一些序列化或者反序列化框架 工作正常,最好还是提供一下无参构造函数

    我们就以gson为例:

    gson 将一个json 字符串 序列化为一个javabean的时候 其实遵循的主要逻辑如下:

    1. 这个class 有没有 无参构造函数,如果有 就使用 这个无参构造函数 来构造出对象 如果没有 去第二步

    2. 第二步 其实就是用 unsafe 来构造出一个 对象。

    来看第一个例子:

    data class KUser(var name:String,var age:Int)
    
    
    fun main(){
        val gson= Gson()
        val person=gson.fromJson<KUser>(""" {"age":"12"} """,KUser::class.java)
    
    
        println(person.name)
    }
    
    
    

    看下输出结果:

    有人觉得奇怪,这里我们json字符串没有 name相关的信息, data class 中 name又定义成了不可空 为啥 没报错

    反而输出的结果是null呢?

    我们修改一下代码:

    data class KUser(var name:String="123",var age:Int)
    
    
    

    再运行,看结果:

    我擦 我都设置了默认值为123了 为啥还是null?

    我不服,我要再改一下,这次我把age的默认值也加上:

    data class KUser(var name:String="123",var age:Int)
    

    这下终于正常了。

    到这里 我来解释下 为啥会有上述的情况

    kotlin中 假设你有n个属性值,那你必须把这n个属性的默认值 都设置了默认值,才会生成默认的无参构造函数,少一个都不会生成无参构造函数

    所以 前面的例子,第一个结果和第二个结果 就很好解释了,因为没有无参构造函数,所以gson的反序列化 走了unsafe 直接构造出了对象,绕过了 kotlin的 非空判定,所以不报错,输出null

    最后一个结果是因为 我把2个属性 name和age 都设置了默认值,所以有了无参构造函数 从而一切正常。

    kotlin 调用 java函数 时 要注意的点

     val gson= Gson()
        val person=gson.fromJson("",KUser::class.java)
        //不报错 正常展示null 
        println(person)
    
    
        val person2:KUser?=gson.fromJson("",KUser::class.java)
        // 声明为可空 自然不报错
        println(person2)
    
    
        //会崩溃  因为 声明的是不可空,但实际返回了空 所以报错
        val person3:KUser=gson.fromJson("",KUser::class.java)
        println(person3)
    
    
    

    主要看下为啥报错,看下反编译:


    作者:vivo祁同伟
    链接:https://juejin.cn/post/6908986604270927885

    关注我获取更多知识或者投稿

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值