Loud failures are better than silent, faulty behavior

Sometimes, small questions lead to big answers. Sometimes these answers are controversial. One such question is “What does this warning about serialVersionUID mean”? All the advice out there basically is for developers who don’t know what’s going on to write code that will ignore errors when something unexpected happens. In my view – this is exactly the wrong approach. The safe way to act is to make sure that your program crashes if you don’t have control.

Java programmers usually get this warning when they write code that looks like this:

<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> MyServlet <span style="color: #000000; font-weight: bold;">extends</span> HttpServlet <span style="color: #009900;">{</span>
 
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> doGet<span style="color: #009900;">(</span>HttpRequest req, HttpResponse resp<span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
    <span style="color: #666666; font-style: italic;">// Some logic goes here</span>
  <span style="color: #009900;">}</span>
<span style="color: #009900;">}</span>

On stackoverflow this question has an answer that is extensive, well-written, accepted and, in my opinion, wrong. In short, the answer just recommends to implement something like the following:

<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> MyServlet <span style="color: #000000; font-weight: bold;">extends</span> HttpServlet <span style="color: #009900;">{</span>
 
  <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">long</span> serialVersionUID <span style="color: #339933;">=</span> 123L<span style="color: #339933;">;</span>

Let’s dig deeper.

The reason we get this warning is that HttpServlet implements the interface Serializable. This was a design flaw in the first version of the servlet-api, and now we’re stuck with it. A serialized object can be written to and read from byte streams, such as a file. Here is an example:

        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file));
        outputStream.writeObject(new Person("Johannes", "Brodwall"));
 
       // ... somewhere else:
 
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
        Object object = inputStream.readObject();
        System.out.println(object);

In this case, everything is fine. But let’s imagine that some time passes between the write and the read. For example, what if we try to read the file with the next version of the program. A version in which the Person class is changed? For example, what if we changed the implementation of Person to store the name as a single field fullName, instead of two fields firstName and lastName?

In this case, we would get an error message like the following:

java.io.InvalidClassException: Person; local class incompatible:
                             stream classdesc serialVersionUID = -4897183855179110397,
                             local class serialVersionUID = -1928642322738440913

In other words: If we had set the serialVersionUID, we could still have read the file. Now we’re stuck.

This is why the stackoverflow answer recommends putting a serialVersionUID field in the class.

But wait, there’s another option. Let’s say we found this problem when we tested if our new version was backwards compatible. Now, we could just cut and paste the serialVersionUID from the stack trace. If we do test our software, this is just as easy as putting the serialVersionUID there in the first place. So, let’s fix the Person class:

<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> Person <span style="color: #000000; font-weight: bold;">implements</span> <span style="color: #003399;">Serializable</span> <span style="color: #009900;">{</span>
 
    <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">long</span> serialVersionUID <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>4897183855179110397L<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">String</span> fullName<span style="color: #339933;">;</span>
 
    <span style="color: #000000; font-weight: bold;">public</span> Person<span style="color: #009900;">(</span><span style="color: #003399;">String</span> firstName, <span style="color: #003399;">String</span> lastName<span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
        <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">fullName</span> <span style="color: #339933;">=</span> firstName <span style="color: #339933;">+</span> <span style="color: #0000ff;">" "</span> <span style="color: #339933;">+</span> lastName<span style="color: #339933;">;</span>
    <span style="color: #009900;">}</span>
 
    @Override
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">String</span> toString<span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
        <span style="color: #000000; font-weight: bold;">return</span> getClass<span style="color: #009900;">(</span><span style="color: #009900;">)</span>.<span style="color: #006633;">getSimpleName</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #339933;">+</span> <span style="color: #0000ff;">"[name="</span> <span style="color: #339933;">+</span> fullName <span style="color: #339933;">+</span> <span style="color: #0000ff;">"]"</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">}</span>
<span style="color: #009900;">}</span>

Voila! Problem fixed. Our code will run again. Here’s the output of printing the object:

Person[name=null]

Whoops! By forcing different versions of class Person to have the same serialVersionUID, the code now has a serious bug. FullName should never be null (especially since it’s final!). And what’s worse, the bug is a silent one. If we don’t examine the contents of System.out (in this case), we might not catch it before it escapes into the wild.

When you’re not sure, the correct behavior should be to fail, not to silently do the wrong thing.

TL;DR

If you omit a serialVersionUID field from your class, many changes will cause serialized objects to no longer be readable. Even for trivial changes.

Sadly, classes like HttpServlet, AbstractAction and JFrame which are meant to be subclassed implements Serializable, even though they are almost never serialized in practice. Adding serialVersionUID to these classes would only be noise.

The serialVersionUID field can be added to a class afterward if you actually want to read old objects in a new version of the program. This leaves you no worse off than if you added the serialVersionUID field in the first place.

If the old and the new version of the class are deeply incompatible, giving the class a serialVersionUID when you first create it will cause silent faulty behavior.

I prefer loud, failing behavior that is easy to detect during testing over quiet, faulty behavior that may escape into production. I think you do, too.

Join the discussion
Be the first to comment on this article. Our Commenting Policies