栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Android Gson在Kotlin data class中的使用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android Gson在Kotlin data class中的使用

文章目录
  • Android Gson在Kotlin data class中的使用
    • 基本使用
    • NEP 空指针异常问题
      • 原因
    • nullSafe 失效问题
    • 默认值失效问题
      • 字段都有默认值
      • 字段部分有默认值
    • 解决问题
      • 使用无参构造函数
      • 声明为字段
      • 使用moshi框架

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
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/863579.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号