Skip to content
Advertisement

Should I implement serialVersionUID in Exception Subclasses?

on a code review I saw Code like this…

public class IntersectionException extends Exception

   static final long serialVersionUID = 42L;

as I know we need to implement serialVersionUID to be sure that we deserialize same version of an Object as serialized. And if we take a look on Hierarchy of class Exception, we will see there is a implementation of “Serialiazable” interface.

But in the class Exception we have already a serialVersionUID. So, my question is, should I declare serialVersionUID in a child class of Exception too?

Advertisement

Answer

This is an idiotic linting rule, so, no, you should not.

Specifically, the rule: “All things that are serializable must have a serialVersionUID” is extremely dubious. Applying this rule to things that are serializable by accident, such as all exception subtypes, is downright stupidity.

Adhering to the linting rule either [A] just straight up making your code quite a bit worse (yes, this linting rule gives you incentive to make your code worse. I’m using the word ‘stupidity’ for good reason), or [B] makes it only slightly worse but writing it ‘semi-properly’ like this is actually really expensive, in the sense that it takes a ton of time to do right, and then your code still is worse than if you just leave it out.

I better back up these drastic claims!

What is serialVersionUID, even?

Whenever you serialize any object using java’s baked in serialization mechanism, the ‘serial version ID’ of a class is written into the data as well. When deserializing, that class needs to be on the classpath, AND it must have the same svUid. If it doesn’t, deserializing throws an exception instead. The svUid of any class is defined as:

  • The value of the static final long serialVersionUID field if it is present,

or if it is not,

  • The result of computing it, which is based on hashing all signature aspects of that class (extends, implements, the type and name of all the fields, the return type, name, and parameter types and names of all methods). If you want to see it, the serialver tool (in the bin dir of any JDK distro) will tell you.

Why this linter rule is bad

The reason that it is boneheaded to complain about this is that the UID part of the serialization mechanism is what it is, and whilst it’s not great, it is actually better than what this linting rule is pushing you to. Specifically, this is the proper model of serial version UID management1:

  • All classes start out with the computed svUid. Do not explicitly add one.
  • When you update your code and you are actually doing the silly move of using serialization to communicate between different versions of the same app, then by default any class is no longer compatible with a previous version of itself if any part of any signature changed.
  • … unless the author has explicitly opted into writing a version update that IS compatible with older versions and has taken the time to write tests and document this behaviour. In this case they obviously have the older version (how else could they make the promise that you can deserialize something ‘saved’ with the old version using the new version, if you don’t even have the old?) – so they run the serialver tool on the old version and explicitly write this into their new version.

The above is obviously (to me anyway) vastly superior to any other approach, if (de)serializing between versions is even relevant (which it really shouldn’t be). Thus, if you go with the rule of ‘do not use java’s serialization mechanism at all’, then the linting rule should be disabled. If you go with the alternate best way to go about it, the linting rule should still be disabled: It is in fact RIGHT that all classes start off without any svUid initially. Your linting tool has no idea that this is version 2 of something that needs to be serialization-compatible with version 1 and therefore no linting rule can exist that triggers only in that (extremely rare) circumstance. Thus, linting rule is bad, disable it.

I can’t, I must add one.

Okay, let’s ignore all that and say that we must add a value for it. But what number shall we put in? There are 3 options.

A constant value, such as 1L

This just changes the conventions: Instead of defaulting to the behaviour of ‘any signature update makes a class incompatible with a previous version of itself – to change that, add svUid’, you’ve now changed that behaviour to ‘any signature change, even adding a field, doesn’t matter – that class is still serialization-compatible with any previous version of it – to change that, add svUid’.

That’s a bad default, obviously. But it is the easy way to do it, further adding fuel to the fire: A linter should, if anything, warn if you use some obvious constant (1, or 42, or 31, or whatnot).

The actual svUid

As in, what serialver would generate. This is superfluous, and therefore a DRY violation. It’s also a lot of work: Write in a dummy, compile it, run serialver, copy it over.

What you want: The svUid of the previous version, IF you are compatible with it

That’s the true answer. Not something a linter tool can help you with, though.


NB: This answer is a slightly edited copy of a reply to a feature request for project lombok asking for precisely this: serialVersionUID for your own exception subtypes. See Project lombok issue #3016.

[1] Actually the best way to do serialization is to not use java’s baked in mechanism at all and instead use a library that thought it through properly.

8 People found this is helpful
Advertisement