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 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android Gson在Kotlin data class中的使用 -> 正文阅读

[移动开发]Android Gson在Kotlin data class中的使用

Android Gson在Kotlin data class中的使用

基本使用

data class UserBean(val name: String, val age: Int)

val json = """
        {
            "name":"小明",
            "age":18
        }
    """.trimIndent()

fun main() {
    val userBean = Gson().fromJson(json, UserBean::class.java)
    println("姓名:${userBean.name} 年龄:${userBean.age}")
}

//姓名:小明 年龄:18

NEP 空指针异常问题

val json2 = """
        {
            "name":null,
            "age":null
        }
    """.trimIndent()

fun main() {
    val userBean = Gson().fromJson(json2, UserBean::class.java)
    println("姓名:${userBean.name} 年龄:${userBean.age}")
    printMsg(userBean.name)
}

fun printMsg(msg: String) {
}

//姓名:null 年龄:0
//Exception in thread "main" java.lang.NullPointerException: Parameter specified as non-null is null: method com.example.lib_java.test.GsonTestKt.printMsg, parameter msg

原因

printMsg()方法翻译为Java代码:

public static final void printMsg(@NotNull String msg) {
    Intrinsics.checkNotNullParameter(msg, "msg");
}

这是因为这段代码,Gson解析后,UserBean中的name字段仍然为null,而Kotlin严格区分可null和非null类型,会做类nullSafe检查,所以在调用printMsg()方法时会抛出NPE。

nullSafe 失效问题

Gson是通过反射创建对象,然后遍历JSON中的key值和UserBean中的字段进行对应赋值的,核心代码如下:

ReflectiveTypeAdapterFactory类

@Override public T read(JsonReader in) throws IOException {
    if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
    }

    //创建对象
    T instance = constructor.construct(); 

    try {
        in.beginObject();
        while (in.hasNext()) {
            String name = in.nextName();
            BoundField field = boundFields.get(name);
            if (field == null || !field.deserialized) {
                in.skipValue();
            } else {
                //赋值操作
                field.read(in, instance);
            }
        }
    } catch (IllegalStateException e) {
        throw new JsonSyntaxException(e);
    } catch (IllegalAccessException e) {
        throw new AssertionError(e);
    }
    in.endObject();
    return instance;
}

ConstructorConstructor类

在这里插入图片描述

  • newDefaultConstructor:通过反射无参构造函数创建对象。
  • newDefaultImplementationConstructor:通过反射Collection和Map等集合框架类型创建对象。
  • newUnsafeAllocator:通过Unsafe包创建对象,兜底方案。

UserBean翻译为Java代码:

public final class UserBean {
    @NotNull
    private final String name;
    private final int age;

    @NotNull
    public final String getName() {
        return this.name;
    }

    public final int getAge() {
        return this.age;
    }

    public UserBean(@NotNull String name, int age) {
        Intrinsics.checkNotNullParameter(name, "name");
        super();
        this.name = name;
        this.age = age;
    }  
}

由此可见,UserBean没有无参构造函数,所以这里是通过Unsafe类创建对象的,不回调用其他的构造函数,也就绕开了Kotlin的nullSafe检查。

默认值失效问题

字段都有默认值

data class UserBean(val name: String = "未知", val age: Int = -1)

val json2 = """
        {

        }
    """.trimIndent()

fun main() {
    val userBean = Gson().fromJson(json2, UserBean::class.java)
    println("姓名:${userBean.name} 年龄:${userBean.age}")
}

//姓名:未知 年龄:-1

翻译为Java代码:

public final class UserBean {
    @NotNull
    private final String name;
    private final int age;

    @NotNull
    public final String getName() {
        return this.name;
    }

    public final int getAge() {
        return this.age;
    }

    public UserBean(@NotNull String name, int age) {
        Intrinsics.checkNotNullParameter(name, "name");
        super();
        this.name = name;
        this.age = age;
    }

    // $FF: synthetic method
    public UserBean(String var1, int var2, int var3, DefaultConstructorMarker var4) {
        if ((var3 & 1) != 0) {
            var1 = "未知";
        }

        if ((var3 & 2) != 0) {
            var2 = -1;
        }

        this(var1, var2);
    }

    public UserBean() {
        this((String)null, 0, 3, (DefaultConstructorMarker)null);
    }
}

UserBean存在无参构造函数,所以走的是newDefaultConstructor()方法流程。

字段部分有默认值

data class UserBean(val name: String = "未知", val age: Int)

val json2 = """
        {
        
        }
    """.trimIndent()

fun main() {
    val userBean = Gson().fromJson(json2, UserBean::class.java)
    println("姓名:${userBean.name} 年龄:${userBean.age}")
}

//姓名:null 年龄:0

翻译为Java代码:

public final class UserBean {
    @NotNull
    private final String name;
    private final int age;

    @NotNull
    public final String getName() {
        return this.name;
    }

    public final int getAge() {
        return this.age;
    }

    public UserBean(@NotNull String name, int age) {
        Intrinsics.checkNotNullParameter(name, "name");
        super();
        this.name = name;
        this.age = age;
    }

    // $FF: synthetic method
    public UserBean(String var1, int var2, int var3, DefaultConstructorMarker var4) {
        if ((var3 & 1) != 0) {
            var1 = "未知";
        }

        this(var1, var2);
    }
}

UserBean类没有无参构造函数,所以这里走的Unsafe类。

解决问题

使用无参构造函数

data class UserBean(val name: String, val age: Int) {
    constructor() : this("未知", -1)
}

val json = """
        {
         "age":18
        }
    """.trimIndent()

fun main() {
    val userBean = Gson().fromJson(json, UserBean::class.java)
    println("姓名:${userBean.name} 年龄:${userBean.age}")
}

//姓名:未知 年龄:18

声明为字段

本质是通过间接创建一个无参构造函数实现的。

class UserBean {
    val name: String = "未知"
    val age: Int = -1
}

val json = """
        {
         "age":18
        }
    """.trimIndent()

fun main() {
    val userBean = Gson().fromJson(json, UserBean::class.java)
    println("姓名:${userBean.name} 年龄:${userBean.age}")
}

//姓名:未知 年龄:18

使用moshi框架

添加依赖

implementation 'com.squareup.moshi:moshi-kotlin:1.11.0'
data class UserBean(val name: String = "未知", val age: Int)

val json = """
        {
         "age":18
        }
    """.trimIndent()

fun main() {
    val moshi = Moshi.Builder()
	    .addLast(KotlinJsonAdapterFactory())
    	.build()
    val jsonAdapter = moshi.adapter(UserBean::class.java)
    val userBean = jsonAdapter.fromJson(json)
    println("姓名:${userBean?.name} 年龄:${userBean?.age}")
}

//姓名:未知 年龄:18
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-05-07 11:18:35  更:2022-05-07 11:19:32 
 
开发: 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/25 0:59:21-

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