Eth*_*hoi 3 android gson kotlin
Foo 数据类可以转换各种类型。
为了有效实现,可使用来实现该属性lazy delegate。但是,当我尝试访问惰性属性时,我遇到了NPE。当我使用转换功能时toBar,不会发生NPE。
//data from Retrofit response via GsonConverter
data class Foo(
@SerializedName("type") val type: String,
@SerializedName("data") val data: JsonElement
) {
val asBar by lazy { // it's throw NPE
Bar.fromJson(data)
}
val asVar by lazy {
Var.fromJson(data)
}
fun toBar() = Bar.fromJson(data)
fun toVar() = Var.fromJson(data)
}
Run Code Online (Sandbox Code Playgroud)
...
override fun onBindViewHolder(
holder: RecyclerView.ViewHolder,
position: Int
) {
when (holder) {
is BarHolder -> getItem(position)?.asBar?.let(holder::bind) // NPE
is VarHolder -> getItem(position)?.asVar?.let(holder::bind) // NPE
//is BarHolder -> getItem(position)?.toBar()?.let(holder::bind) // it's work
//is VarHolder -> getItem(position)?.toVar()?.let(holder::bind) // it's work
}
}
Run Code Online (Sandbox Code Playgroud)
java.lang.NullPointerException:尝试在空对象引用上调用接口方法'java.lang.Object kotlin.Lazy.getValue()'
为什么发生NPE?怎么解决呢?
问题在于Gson在反序列化JSON时实例化类的方式。Gson Unsafe在以下语言中使用Java UnsafeAllocator:
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
final Object unsafe = f.get(null);
final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
return new UnsafeAllocator() {
@Override
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception {
assertInstantiable(c);
return (T) allocateInstance.invoke(unsafe, c); // instantiation of the class
}
}
Run Code Online (Sandbox Code Playgroud)
调用allocateInstance.invoke(unsafe, c)所做的只是为类分配内存,而无需调用其构造函数。当实例化该类时,Gson使用反射来设置其字段。
现在回到科特林和lazy代表。该lazy { }制造商实际上创建了一个Lazy<T>对象。该方法在类初始化期间即在其构造函数被调用之后被调用。
因此,如果在不安全的分配过程中未调用构造函数,Lazy<T>则将不会创建委托,并且将保留一个null值。对委托属性的每次访问都会调用getValue()委托,在这种情况下会导致NullPointerException。
要解决此问题,您可以使用已经定义的方法(toBar()和toVar()),也可以创建计算的属性asBar,asVar而不是懒惰的属性:
val asBar
get() = Bar.fromJson(data)
val asVar
get() = Var.fromJson(data)
Run Code Online (Sandbox Code Playgroud)
但是,也许更好的解决方案是将Foo类保留为数据的哑包装,并将转换逻辑移到外部。
| 归档时间: |
|
| 查看次数: |
186 次 |
| 最近记录: |