Reputation: 17713
I have a Java serialization question that is neither reported nor negated as problematic in Java Docs (for JDK8).
So, suppose I have a class that implements Serializable
private static final long serialVersionUID = 1L;
private List<CharSequence> values;
If I change values
to
private List<String> values;
should I change the serialVersionUID
?
Upvotes: 3
Views: 151
Reputation: 147154
If you want to make an incompatible change you should change the serialVersionUID
so the you get an appropriate (if not nice) error instead of, say, a ClassCastException
at some point further on.
If you want to maintain compatibility, keep the serialVersionUID
and add a custom readObject
method to convert the List<CharSequence>
to List<String>
.
Edit: To expand on that, if you want compatibility, your code should look something like this:
// final - for one thing I don't want to implement readObjectNoData
public final class JeanObject {
If this class was added as a subclass to another class then there would be "no data" in legacy serialised streams.
private static final long serialVersionUID = 1L;
// Mutable field for Java Serialization, but treat as final.
private List<String> values = new ArrayList<>();
If you want values
to be final
you will need a degenerate Serialisation Proxy hack where the proxy object is of the same type.
I am going to assume you want the collection object to be mutable. You probably shouldn't serialise mutable objects, but that's how it's usually used and how the OP's example feels.
private void readObject(
ObjectInputStream in
) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField fields = in.readFields();
@SuppressWarnings("unchecked")
var oldValues = (List<CharSequence>)fields.get("values", null);
// Don't bother with nice error message.
// NPE if missing or null elements.
this.values = oldValues.stream().collect(
ArrayList::new,
(values, value) -> values.add(value.toString()),
List::addAll
);
}
// ...
An alternative to the (very functional-looking) 3-arg Stream.collect
is to use Collectors.mapping
. An old-fashioned posh for loop would be less confusing all round, but that's not showing off on the internet - you'd look like someone who just gets stuff done.
Collectors.toList
is probably not suitable as, say, adding an elements to the list someway down the line may result in an error. Maybe not today. Maybe not tomorrow. But when your successor's automatic updates the implementation. And they will hate you for the rest of their life. Here's the API docs talking:
There are no guarantees on the type, mutability, serializability, or thread-safety of the
List
returned
Who wants that?
Edit: Reading that quote again, you might not be able to serialise your deserialised object if you use toList
...
(Code compiles but, as ever, is untested.)
Upvotes: 2
Reputation: 140514
Another way to think of this serialization/deserialization operation is that it's a bit like casting.
Can you cast a List<CharSequence>
to a List<String>
? Not directly: the compiler will stop you, because generics are invariant. You can force it, however:
List<String> list = (List<String>) (List<?>) listOfCharSequences;
String entry = list.get(0);
and this might work a lot of the time, because, String
is the most common CharSequence
.
But it's possible that there's something else in there which is a CharSequence
, but not a String
, for example a StringBuilder
. The code above would crash with a ClassCastException
.
So, if you can be sure that all of your CharSequence
s are genuinely String
s, it's safe to make this change. Otherwise, you should either guard against it by changing the serial version id; or, in the case of String
, you could simply invoke toString()
on all of the elements:
List<String> listOfStrings = listOfCharSequences.stream()
.map(cs -> Objects.toString(cs, null))
.collect(toList())
(String.toString()
returns itself, so this would return no new instance for elements that are already String
s)
Upvotes: 3
Reputation: 856
It is completely up to you. When you are specifying a serialVersionUID you are taking over the responsibility to change it when needed. If your system is in risk of encountering various versions of this object when you need to change it.
Upvotes: -2