![](/img/trans.png)
[英]Why can a java nullable returned value be assigned to a Kotlin Non-Nullable variable?
[英]Why Kotlin data classes can have nulls in non-nullable fields with Gson?
在 Kotlin 中,您可以創建一個data class
:
data class CountriesResponse(
val count: Int,
val countries: List<Country>,
val error: String)
然后您可以使用它來解析 JSON,例如“{n: 10}”。 在這種情況下,您將有一個對象val countries: CountriesResponse
,從Retrofit
、 Fuel
或Gson
接收,其中包含以下值: count = 0, countries = null, error = null
。
在Kotlin + Gson - How to get an emptyList when null for data class你可以看到另一個例子。
當您稍后嘗試使用countries
,您將在此處得到一個異常: val size = countries.countries.size
: "kotlin.TypeCastException: null cannot be cast to non-null type kotlin.Int"。 如果您編寫代碼並使用?
訪問這些字段時,Android Studio 會突出顯示?.
並警告: Unnecessary safe call on a non-null receiver of type List<Country>
。
那么,我們應該使用?
在數據類中? 為什么應用程序可以在運行時將null
設置為不可為 null 的變量?
發生這種情況是因為 Gson 使用不安全(如在java.misc.Unsafe
)實例構造機制來創建類的實例,繞過它們的構造函數,然后直接設置它們的字段。
有關一些研究,請參閱此問答: Gson Deserialization with Kotlin, Initializer block not called 。
因此,Gson 忽略了構造邏輯和類狀態不變量,因此不建議將其用於可能受此影響的復雜類。 它也會忽略 setter 中的值檢查。
考慮 Kotlin-aware 序列化解決方案,例如 Jackson(在上面鏈接的問答中提到)或kotlinx.serialization
。
許多 Json 解析器(包括 Gson)的設計決定是讓它默默地吞下不存在的字段。 對象被設置為null
,原語被設置為0
,完全掩蓋了 API 錯誤並導致更下游的神秘錯誤。
出於這個原因,我推薦兩階段解析:
package com.example.transport
//this class is passed to Gson (or any other parser)
data class CountriesResponseTransport(
val count: Int?,
val countries: List<CountryTransport>?,
val error: String?){
fun toDomain() = CountriesResponse(
count ?: throw MandatoryIsNullException("count"),
countries?.map{it.toDomain()} ?: throw MandatoryIsNullException("countries"),
error ?: throw MandatoryIsNullException("error")
)
}
package com.example.domain
//this one is actually used in the app
data class CountriesResponse(
val count: Int,
val countries: Collection<Country>,
val error: String)
是的,它的工作量是原來的兩倍 - 但它會立即查明 API 錯誤,並在您無法修復這些錯誤時為您提供處理這些錯誤的地方,例如:
fun toDomain() = CountriesResponse(
count ?: countries?.count ?: -1, //just to brag we can default to non-zero
countries?.map{it.toDomain()} ?: ArrayList()
error ?: MyApplication.INSTANCE.getDeafultErrorMessage()
)
是的,您可以使用具有更多選項的更好的解析器 - 但您不應該這樣做。 您應該做的是將解析器抽象出來,以便您可以使用任何解析器。 因為無論您今天找到多么先進和可配置的解析器,最終您都會需要它不支持的功能。 這就是我將 Gson 視為最小公分母的原因。
有一篇文章解釋了在存儲庫模式的更大上下文中使用(和擴展)的這個概念。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.