I currently have class which have some fields initialized in declaration, like this:
public class SomeClass implements Externalizable {
private long id;
private final List<Hit> hits = new ArrayList<>();
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeLong(id);
out.writeInt(hits.size());
for (int i = 0; i < hits.size(); i++) {
out.writeObject(hits.get(i));
}
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
id = in.readLong();
int size = in.readInt();
for (int i = 0; i < size; i++) {
hits.add((Hit) in.readObject()); //<--Nullpointer here, hits == null
}
}
}
And this class is used in filebased chronicle-map
configured like this:
ChronicleMap<Long, SomeClass> storage = ChronicleMapBuilder
.of(Long.class, SomeClass.class)
.averageValueSize(avgEntrySize)
.entries(entries)
.createPersistedTo(new File(path));
The problem is that when I restart my application I get NullpointerException
when chronicle tries to read saved map, because hits
field wasn't initialized, meaning it is null
.
I did some investigation and found that before calling readExternal
chronical creates object of this class using UNSAFE.allocateInstance
(in ExternalizableMarshaller
):
protected E getInstance() throws Exception {
return (E) NativeBytes.UNSAFE.allocateInstance(classMarshaled);
}
So basically this is the reason why its not initialized. What I am trying to understand why it is using such approach instead of MethodHandle
or reflection?
And maybe there is another way to fix this without modifying SomeClass
, like some chronicle configuration property maybe?