Kotlin throw NPE when using Lazy Delegate with Gson
The problem lies in the way Gson instantiates classes while deserializing JSON. Gson uses Java's Unsafe
in the 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
}
}
What the call allocateInstance.invoke(unsafe, c)
does is simply allocate the memory for the class without invoking its constructor. When the class is instantiated, Gson uses reflection to set its fields.
Now back to Kotlin and the lazy
delegate. The lazy { }
builder actually creates a Lazy<T>
object. The method is invoked during the class initialization, i.e. after its constructor has been called.
So, if the constructor isn't invoked during the unsafe allocation, the Lazy<T>
delegate won't be created and will hold a null
value. Every access to the delegated property calls getValue()
on the delegate and in this case results in a NullPointerException
.
To solve it you can either use the methods you've already defined (toBar()
and toVar()
) or create computed properties asBar
and asVar
instead of lazy ones:
val asBar
get() = Bar.fromJson(data)
val asVar
get() = Var.fromJson(data)
However, maybe the better solution would be to leave the Foo
class as a dumb wrapper for the data and move the converting logic outside.