问题在于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 newInstance(Class c) throws Exception {
assertInstantiable(c);
return (T) allocateInstance.invoke(unsafe, c); // instantiation of the class
}
}
调用allocateInstance.invoke(unsafe, c)
所做的只是为类分配内存,而无需调用其构造函数。当实例化该类时,Gson使用反射来设置其字段。
现在回到科特林和lazy
代表。该lazy { }
制造商实际上创建了一个Lazy
对象。该方法在类初始化期间即在其构造函数被调用之后被调用。
因此,如果在不安全的分配过程中未调用构造函数,Lazy
则将不会创建委托,并且将保留一个null
值。对委托属性的每次访问都会调用getValue()
委托,在这种情况下会导致NullPointerException
。
要解决此问题,您可以使用已经定义的方法(toBar()
和toVar()
),也可以创建计算的属性asBar
,asVar
而不是懒惰的属性:
val asBar get() = Bar.fromJson(data) val asVar get() = Var.fromJson(data)
但是,也许更好的解决方案是将Foo
类保留为数据的哑包装,并将转换逻辑移到外部。
问题在于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 newInstance(Class c) throws Exception {
assertInstantiable(c);
return (T) allocateInstance.invoke(unsafe, c); // instantiation of the class
}
}
调用allocateInstance.invoke(unsafe, c)
所做的只是为类分配内存,而无需调用其构造函数。当实例化该类时,Gson使用反射来设置其字段。
现在回到科特林和lazy
代表。该lazy { }
制造商实际上创建了一个Lazy
对象。该方法在类初始化期间即在其构造函数被调用之后被调用。
因此,如果在不安全的分配过程中未调用构造函数,Lazy
则将不会创建委托,并且将保留一个null
值。对委托属性的每次访问都会调用getValue()
委托,在这种情况下会导致NullPointerException
。
要解决此问题,您可以使用已经定义的方法(toBar()
和toVar()
),也可以创建计算的属性asBar
,asVar
而不是懒惰的属性:
val asBar get() = Bar.fromJson(data) val asVar get() = Var.fromJson(data)
但是,也许更好的解决方案是将Foo
类保留为数据的哑包装,并将转换逻辑移到外部。