NPE在Kotlin数据类中使用@Parcelize注释



我最近将Java Models类迁移到Kotlin Data类。我使用@Parcelize注释来避免Parcelable的样板代码。我在Kotlin的数据类如下所示,

@Parcelize
data class TimeLine(
@SerializedName("submittedOnDate")
var submittedOnDate: List<Int> = ArrayList(),
@SerializedName("submittedByUsername")
var submittedByUsername: String,
@SerializedName("submittedByFirstname")
var submittedByFirstname: String,
@SerializedName("submittedByLastname")
var submittedByLastname: String,
@SerializedName("approvedOnDate")
var approvedOnDate: List<Int> = ArrayList(),
@SerializedName("approvedByUsername")
var approvedByUsername: String,
@SerializedName("approvedByFirstname")
var approvedByFirstname: String,
@SerializedName("approvedByLastname")
var approvedByLastname: String,
@SerializedName("activatedOnDate")
var activatedOnDate: List<Int>,
@SerializedName("activatedByUsername")
var activatedByUsername: String,
@SerializedName("activatedByFirstname")
var activatedByFirstname: String,
@SerializedName("activatedByLastname")
var activatedByLastname: String,
@SerializedName("closedOnDate")
var closedOnDate: List<Int>) : Parcelable`

但它给了我空指针错误,如下所示,

java.lang.NullPointerException: Attempt to invoke interface method 'int 
java.util.Collection.size()' on a null object reference at 
org.demo.mobile.models.accounts.savings.TimeLine.writeToParcel(TimeLine.kt:0) 
at  org.demo.mobile.models.accounts.savings.SavingAccount.writeToParcel(SavingAccount.kt:0)

我不知道为什么它显示NullPointerException,因为Java模型运行良好。我该如何修复它?NPE背后的原因是什么?

我遇到过这个问题,发现我的数据类上的一个不可为null的属性在json上接收到一个null值。

我的数据类:

@Parcelize
data class Person(
@SerializedName("first_name") val firstName: String,
@SerializedName("middle_name") val middleName: String,
@SerializedName("last_name") val lastName: String,
@SerializedName("birth_date") val birth: String,
@SerializedName("education") val education: List<Education> // <-- non-nullable property
) : Parcelable {
@Parcelize
data class Education(
@SerializedName("course") val course: String?,
@SerializedName("school") val school: String?,
) : Parcelable
}

JSON:

[
{
"first_name": "Steve",
"middle_name": "James",
"last_name": "Roberson",
"birth_date": "March 28, 1960",
"education": [
{
"course": "Course 1",
"school": "School 1",
},
{
"course": "Course 2",
"school": "School 2",
}
]
},
{
"first_name": "Michael",
"middle_name": "Ong",
"last_name": "Reyes",
"birth_date": "March 28, 1960",
"education": null // <-- this caused the error
},
...
]

因此,我通过添加?将该属性声明为可为null的类型,它解决了这个问题。

@SerializedName("education") val education: List<Education>?

在您的情况下,我相信您试图解析的JSON有一个导致异常的null值。

GSON使用不安全的API来构造对象-它可以将null放在您声明的不可为null的类型的位置。生成的Parcelable实现需要非null值。当您尝试将对象放置到地块时,它会崩溃。

我看到两个选项:

1( 获取实例后手动调整值

您已将所有属性声明为var。您可以制作一个扩展方法,为您修复非null值。

@Suppress("SENSELESS_COMPARISON")
fun TimeLine.fixNulls() {
if (submittedOnDate == null) submittedOnDate = emptyList()
if (approvedOnDate == null) submittedOnDate = emptyList()
if (activatedOnDate == null) submittedOnDate = emptyList()
if (closedOnDate == null) submittedOnDate = emptyList()
}

示例:

val list = gson.fromJson(...).forEach { it.fixNulls() }

或者,如果您有一个具有val属性的不可变类型:

fun TimeLine.withFixedNulls() = copy(
submittedOnDate = submittedOnDate ?: emptyList(),
approvedOnDate = approvedOnDate ?: emptyList(),
activatedOnDate = activatedOnDate ?: emptyList(),
closedOnDate = closedOnDate ?: emptyList()
)

示例:

val list = gson.fromJson(...).map { it.withFixedNulls() }

不利的一面是,您必须考虑这一点,并记住在从GSON获得的每个对象上调用它。

这两种变体都有其他问题(内存消耗、并发(。

2( 使用考虑到Kotlin构建的序列化库

您可以使用Moshi Kotlin Codegen。Moshi是一个与GSON类似的序列化库,但其Kotlin Codegen注释处理器将为您的类型生成特殊的JSON适配器,如果您试图将null反序列化为不可为null的属性,这些适配器将引发异常。

当您尝试反序列化无效的JSON时,它就会抛出异常,而不是当您尝试使用Kotlin/Java对象时。

免责声明:我只使用了Moshi+Kotshi,它的工作原理相同。

我将把实施作为练习留给你。

思考的食粮

您不应该将大型对象或大量对象放入Parcel/Intent。有一个尺寸限制。也许你应该使用数据库。

@Parcelize            
data class TimeLine(
@field:SerializedName("via")
val via: MutableList<Int>? = null,
@field:SerializedName("submittedByUsername")
val submittedByUsername: String?= null
......... so on... 
):Parcelable

您的数据模型类与此类似。您需要添加吗?(Null安全(运算符,以便它接受Null值。有关零安全的更多详细信息,请访问Kotlin官方文档

最新更新