Reputation: 6069
I had an object
public class Foo implements Serializable {
private List<Bar> barList;
// getters and setters
}
(Bar
also implements Serializable.)
FindBugs threw a violation SE_BAD_FIELD
. Which is fair enough, because List
is not transient
or Serializable
. However, I want to serialize this list! So I'm going to need to write writeObject
and readObject
myself.
I had a look at the ArrayList
implementation of writeObject
in the JDK, which is fairly straightforward but also a little more complicated than I thought. Here it is:
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
I had expected it would just do the part called "Write out all elements in the proper order" above. But it seems to be a bit more complex than that. Do I need to copy the stuff into my implementation of writeObject
inside Foo
?
readObject
implementation.modCount
behaviour given that I don't have access to the protected modCount
field? Should I put writeObject
inside a synchronized
block?Thanks!
Upvotes: 0
Views: 115
Reputation: 11
If barList
is private and doesn't need to be set verbatim, a quick solution is to declare it as some serializable type like private ArrayList<Bar> barList
and modify it with something like:
void addBar(Bar bar) { barList.add(bar); }
void setBarList(List<Bar> bars) { barList = new ArrayList(bars); }
But if you have to write serialization yourself for some reason, you need to write the size and each object (use Externalizable, it's faster than just Serializable
). Based on example from Oracle:
class Foo implements Externalizable {
List<Bar> barList;
public void writeExternal(ObjectOutput out) throws IOException {
if (barList == null) {
out.writeInt(-1);
return;
}
out.writeInt(barList.size());
for (Bar bar : barList) {
out.writeObject(bar);
}
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
int size = in.readInt();
if (size >= 0) {
barList = new ArrayList<>();
for (int i = 0; i < size; i++) {
barList.add((Bar) in.readObject());
}
}
}
}
Note that readExternal()
still uses ArrayList
. If you want to preserve the class of the list, you can also write class name in writeExternal()
and instantiate it using reflection in readExternal()
. That would require a no-args constructor in that class though.
Upvotes: 1
Reputation: 691933
FindBugs is warning you, because the actual concrete List might not be Serializable. That doesn't mean it isn't. If you're absolutely sure that the actual list is indeed, at runtime, serializable (as ArrayList, LinkedList and many other List implementations are), then you don't need to do anything.
A good way to ensure about that is to avoid accepting any List as argument from the outside:
// ArrayList is serializable
private List<Bar> barList = new ArrayList<>();
public void setBarList(List<Bar> barList) {
// barList stays an ArrayList, still serializable
this.barList.clear();
this.barList.addAll(barList);
}
Upvotes: 0