深入理解Kotlin无参构造函数

2023-05-16

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入理解Kotlin无参构造函数 的相关文章

随机推荐

  • 【已解决】element-ui组件嵌套太多层,导致内部el-input和el-select 等组件无法正常输入解决方案

    如果 element ui 组件嵌套太多层 xff0c 可能会导致内部的 el input 和 el select 等组件无法正常输入 出现这种问题通常是由于 z index 属性设置不正确导致的 解决这个问题的方法是调整组件的 z ind
  • 两个半天一刷Ubuntu入门指令

    目录 两个半天一刷Ubuntu入门指令 一 Ubuntu初次体验 二 设备驱动 三 Ubuntu用户组及其权限分配 四 压缩解压 五 Linux连接文件 六 vim编辑器 七 Linux下C编程 八 gcc和make及其Makefile的引
  • ENV环境配置及其下载网络组件包问题

    准备工作 Env 工具包含了 RT Thread 源代码开发编译环境和软件包管理系统 从 RT Thread 官网下载 Env 工具 在电脑上装好 git xff0c 软件包管理功能需要 git 的支持 git 的下载地址为https gi
  • 芯片flash保护(解锁)

    报错结果 Error while accessing a target resource The resource is perhaps notavailable 就是无法下载程序 解决办法 1 下载ST LINK Utility 链接 x
  • 2.RTT-点灯大师的修炼

    1 创建工程模板 相信大家通过学习上一篇文章 1 RTT 环境搭建 现在能熟练的创建一个标准模板了 xff0c 如果不会就跳回去学习一下吧 链接 xff1a 1 RTT 环境搭建 嵌入式路上的流浪的博客 CSDN博客 建立好的工程模板编译并
  • 4.RTT-UART-中断接收及轮询发送

    本期博客开始分享RTT的UART xff0c 利用战舰V3的uart2来输入输出一些字符串 UART xff08 Universal Asynchronous Receiver Transmitter xff09 通用异步收发传输器 xff
  • 12.RTT-IIC设备-AHT10温湿度传感器

    本系列博客更新结束啦 xff01 完结啦 xff01 xff01 xff01 撒花 xff01 xff01 xff01 关于RTT的设备和驱动专题更新完毕啦 xff0c 本期是最后一期 一段学习旅途的结束意味着下一段学习冒险的开始 虽然本系
  • RTduino+sht31温湿度传感器

    本次博客使用的是STM32F103C8T6 xff0c 因为该BSP已经对接好RTduino了可以直接上手使用 一 RTduino简介 RTduino是RT Thread实时操作系统的Arduino生态兼容层 xff0c 为RT Threa
  • 一文揭秘字节跳动、华为、京东的薪资职级

    声明 xff1a 本文所有数字均不是官方数据 xff0c 为网络资料收集整理 字节跳动 01 全球员工总数 字节的员工数量目前超过5万人 图片来源 xff1a 字节范 02 岗位职级 字节跳动的职级研发序列一共10级 xff1a 字节跳动创
  • ESP_C3在ubuntu下运行RT-Thread

    1 clone源代码RT Thread git clone git 64 github com RT Thread rt thread git 2 开始搭建ESP IDF环境 进入源码到bsp文件夹下找到ESP32 C3 xff0c 开始配
  • uniapp中使用弹出层

    uniapp中使用弹出层 因为业务的需要 xff0c 需要弹出一个复选框 xff0c 使用uniapp中自带的框架 使用 xff1a 第一步 xff1a 下载下示例项目 xff0c 找到主要的文件夹 第二步 xff1a 将该文件夹放到组件的
  • 手写一个uniapp的步骤条组件

    span class token operator lt span template span class token operator gt span span class token operator lt span view span
  • uniapp中的分页

    数据量过多就会使用分页 第一种 xff1a API span class token comment 和data同级 span span class token function variable function onReachBotto
  • uniapp中生成随机的二维码并进行保存

    需求 xff1a 需要根据用户id的不同生成不同的二维码 xff0c 并进行本地保存 第一步 xff1a 下载插件 这里对于二维码的生成 xff0c 使用的是第三方插件weapp qrcode min js xff0c 主要用到的文件是 d
  • Pc端的基本Echarts

    Pc端的基本Echarts 双环传态图组件 span class token operator lt span template span class token operator gt span span class token oper
  • el-table表格的sortable排序的使用以及出现小数、%排序错乱

    前端实现排序 xff1a 只需要在表头上加上一个sortable属性即可 span class token tag span class token tag span class token punctuation lt span el t
  • 浏览器的回退和导航栏的选中转态不同步,路由在新窗口打开

    问题1 xff1a 浏览器的回退和导航栏的选中状态不能同步的问题 问题 xff1a 用户后退时候 xff0c 左边导航栏显示的还是上一个页面的导航 xff0c 但是路由和页面已经变了 span class token operator lt
  • 左右联动-左侧点击相应的位置,右侧随之滚动

    第一步 xff1a npm下载 npm install better scroll save 第二步 xff1a 局部注册 xff08 当前组件 xff09 span class token keyword import span BScr
  • 杂技-各种css小技巧

    渐变字体 background image webkit linear gradient bottom 379ED7 0FE2EE webkit background clip text webkit text fill color tra
  • 深入理解Kotlin无参构造函数

    Unsafe 创建实例 在java中 创建一个对象 其实主要就是3种方法 通过new 关键字来创建 这种是最常见的 通过反射构造方法来创建对象 这种也不少见 很多框架中都有使用 Unsafe类来创建实例 xff0c 这种情况非常少见 这里先