@Serializable

12,518 views
Skip to first unread message

Marius Kruger

unread,
May 12, 2012, 7:35:53 PM5/12/12
to project...@googlegroups.com
Hi all, 

some annoying boilerplate that you may be able to help with:

public class Foo implements Serializable {
    private static final long serialVersionUID = 1L;
}

would IMHO look much better as:

@Serializable
public class Foo {
}

PS.  @Accessors(fluent = true) Rocks! please promote it to stable ASAP!

--
✝ Marius

Fabrizio Giudici

unread,
May 12, 2012, 7:42:06 PM5/12/12
to project...@googlegroups.com
On Sun, 13 May 2012 01:35:53 +0200, Marius Kruger <ama...@gmail.com> wrote:

> Hi all,
>
> some annoying boilerplate that you may be able to help with:
>
> public class Foo implements Serializable {
> private static final long serialVersionUID = 1L;
> }
>
> would IMHO look much better as:
>
> @Serializable
> public class Foo {
> }

But serialVersionUID shouldn't be always 1L. It's a quick shortcut that
one can use at the beginning of the life of a class, but it should be
changed when the class changes in a backward incompatible way.


--
Fabrizio Giudici - Java Architect, Project Manager
Tidalwave s.a.s. - "We make Java work. Everywhere."
fabrizio...@tidalwave.it
http://tidalwave.it - http://fabriziogiudici.it

Philip Healy

unread,
May 13, 2012, 12:55:13 PM5/13/12
to project...@googlegroups.com
Hello all,

I agree with Marius about serialVersionUID; it has always struck me as
ugly boilerplate, exactly the type of thing that Lombok is designed to
eliminate. As Fabrizio has pointed out the issue is more complex than
it might appear at first glance. Perhaps version numbers could be
derived automatically based on the structure of the class. Of course,
setting the version number explicitly could also be supported via an
annotation parameter.

P.
> --
> You received this message because you are subscribed to the Google
> Groups group for http://projectlombok.org/
>
> To post to this group, send email to project...@googlegroups.com
> To unsubscribe from this group, send email to
> project-lombo...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/project-lombok?hl=en

Fabrizio Giudici

unread,
May 13, 2012, 2:35:53 PM5/13/12
to project...@googlegroups.com, Philip Healy
On Sun, 13 May 2012 18:55:13 +0200, Philip Healy <philip...@gmail.com>
wrote:

> Of course,
> setting the version number explicitly could also be supported via an
> annotation parameter.

I think it can be surely done. I don't think a
@Serialiable(3454363453463L) makes code so much more readable than the
classic code, in any case I'm not against it. Using an annotation in place
of a tag interface makes sense to me.

Philip Healy

unread,
May 13, 2012, 4:41:02 PM5/13/12
to project...@googlegroups.com
The algorithm for calculating default serialVersionUIDs can be found here:

http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100

Are any of the Lombok devs interested in pursuing this?

P.

Robbert Jan Grootjans

unread,
May 13, 2012, 4:59:54 PM5/13/12
to project...@googlegroups.com
I fully agree that the @Serializable syntax is more readable/less
boilerplate. And the java-spec on serialVersionUID is pretty easy to
follow. But if lombok would compile the serialVersionUID, it would,
arguably, have to ensure that if the class doesn't change, the
serialVersionUID won't either. Take in mind that lombok does some
magic to your class in the background, say, for instance, for lazy
getters (i.e. @Getter(lazy=true)). What happens if you want to make
them non-lazy? What happens there is a change to the the underlying
implementation of lazy getter? Should lombok ensure that the
serialVersionUID stays the same? Or should it always be based on the
class sans-lombok (breaking backwards compatibility in some current
classes).

Just my 0.02 €

Philip Healy

unread,
May 13, 2012, 5:12:40 PM5/13/12
to project...@googlegroups.com
Hi Robbert,

in the case of lazy getters I guess it depends on whether the
additional variables that Lombok creates are declared to be transient
or not. If they are non-transient then the wire protocol is changed
and a change to the serialVersionUID is justified.

P.

Robbert Jan Grootjans

unread,
May 13, 2012, 5:45:00 PM5/13/12
to project...@googlegroups.com
Hey Philip,

I think at this moment, the lazy getter only works on private final
field, it takes these fields and replaces them with an
AtomicReference. (This is not the way it is described in the
documentation). So, putting @Getter(lazy-true) on fields would
effectively change the serialization format. Of course, the easiest
way would be to just change the serialVersionUID ;). Of course, by
default, java already generates a serialVersionUID based on your class
structure (as in the aforementioned document). If changes to the
serialVersionUID aren't a big issue, this should be good enough too.
Of course, this still leaves some people with pesky IDE warnings. In
that case, we could just add an @SuppressWarnings( "serial" ) to the
class.

But, personally, I think just changing the serialVersionId does would
seem unexpected for those that do not know the inner-workings of
lombok.

Roel Spilker

unread,
May 14, 2012, 4:00:44 AM5/14/12
to project...@googlegroups.com
We've looked at @Serial before and it turns out that the gain over the
regular java is not that big. Supporting serialization, especially over
versions, is a big commitment, and therefore warrants a line like
private static final long serialVersionUID = x. It is just one line and
an extra few letters in the implements clause.

If you don't care about maintaining it over several versions you can
actually omit the whole serialVersionUID line. So then there is no gain
at all:

@Serial
class Foo {}

Versus

class Foo implements Serializable {}

The reason most people complain about Serializable is because Eclipse
has the defaults WRONG. Don't set the check about missing
serialVersionUID to WARNING but to IGNORE, and you'll be fine

Marius Kruger

unread,
May 15, 2012, 7:34:19 AM5/15/12
to project...@googlegroups.com
On 14 May 2012 10:00, Roel Spilker <r.sp...@topdesk.com> wrote:
... 
The reason most people complain about Serializable is because Eclipse has the defaults WRONG. Don't set the check about missing serialVersionUID to WARNING but to IGNORE, and you'll be fine

awesome thanks!

--
✝ Marius

Matthew Jaggard

unread,
May 15, 2012, 9:36:47 AM5/15/12
to project...@googlegroups.com

I think part of the problem here is that Java made a bad mistake on the design. They managed to avoid "magic" names nearly everywhere except serialisation. Also the name is so long that I can never remember it - it's not worth me trying.

I agree that eclipse and netbeans shouldn't warn for this being missing by default.

--

Reinier Zwitserloot

unread,
May 21, 2012, 3:21:14 PM5/21/12
to project...@googlegroups.com
Our basic opinion about this is very simple:

Fix your IDE. 99.9999% of all your classes should not have a serialversionUID in the first place. Unfortunately eclipse whines by default, but you can turn that off. In the exceedingly unlikely scenario you need to keep backwards compatibility (serialization-wise) for a class whose wire structure changed, then use the serialversionuid tool to calculate the old one and stick that in the newer version (and write a bunch of code to make sure this claimed wire compatibility is actually true).

Nevertheless, the stated solution is possibly acceptable (I'm on the fence, the default SHOULD be to be wire-dependent, and if @lombok.Serializable is just shorthand for 'implements java.io.Serializable' I don't see how this helps, and then the @Serializable(UID) version is exceedingly rare and thus does not qualify as the kind of boilerplate lombok should fix) - however, generating new fields is extremely fragile in eclipse, so we do not currently intend to spend the resources on it to build this thing.

For those interested in writing patches: Any patch to make generating fields in eclipse better is something we'd be very interested in, but keep in mind that we will probably deny a patch for @Serializable if it defaults to 1L).


On Tuesday, May 15, 2012 3:36:47 PM UTC+2, Mat Jaggard wrote:

I think part of the problem here is that Java made a bad mistake on the design. They managed to avoid "magic" names nearly everywhere except serialisation. Also the name is so long that I can never remember it - it's not worth me trying.

I agree that eclipse and netbeans shouldn't warn for this being missing by default.

On May 15, 2012 12:34 PM, "Marius Kruger" <ama...@gmail.com> wrote:
On 14 May 2012 10:00, Roel Spilker <r.sp...@topdesk.com> wrote:
... 
The reason most people complain about Serializable is because Eclipse has the defaults WRONG. Don't set the check about missing serialVersionUID to WARNING but to IGNORE, and you'll be fine

awesome thanks!

--
✝ Marius

--
You received this message because you are subscribed to the Google
Groups group for http://projectlombok.org/
 
To post to this group, send email to project-lombok@googlegroups.com

To unsubscribe from this group, send email to

Sam Halliday

unread,
Jul 2, 2012, 8:46:21 AM7/2/12
to project...@googlegroups.com
Why not just use @SuppressWarnings("serial") if its IDE warnings you want to suppress? The ID will be generated automatically at runtime.

Reinier Zwitserloot

unread,
Jul 8, 2012, 9:10:52 AM7/8/12
to project...@googlegroups.com
This is the first time I ever heard of it, which goes to show you something :P

I had a casual look at the javadoc and it seems like we can do this.

On Friday, July 6, 2012 3:22:22 PM UTC+1, Tumi wrote:
I would suggest @Externalize instead of @Serial, I mean:

- inject the "implements java.io.Extenalizable" clause
- inject the writeExternal and readExternal methods, which woudl call the right writeXXX/readXXX for every non-transient field of the class of its ObjectOutput / ObjectInput argument

As you know, the default serialization mechanism in Java since 1.1 handles classes implementing Externalizable (which in turns extends Serializable) in a way that can really-really-really faster that the default. This is a big improvement for every application that send data  via JMS or RMI / EJB remoting, and so on.

And... Lombok is the perfect solution to implement Externalizable easily.

P.S: interstingly, nowadays not may people know about the Externalizable alterative to Serializable, surely because the "boilerplate" that would involve making every data class Externalizable doesn't worh it

What do you think? do web fill a suggest-for-enhacement?

Cheers

Maaartin G

unread,
Jul 8, 2012, 12:41:25 PM7/8/12
to project...@googlegroups.com
Actually, Extenalizable as such is unusable with final fields. Serializable overcomes it by some Unsafe tricks, which you shouldn't do in `readExternal`. AFAIK the only clean solution is to use `writeReplace` and `readResolve`.

So even more boilerplate is needed and Lombok could help here even more. I'm afraid that the proper generated code for

@RequiredArgsConstructor @Getter @Externalize
public class ImmutableComplex implements Serializable {
private final double re, im;
}

looks like

@RequiredArgsConstructor @Getter
public class ImmutableComplex implements Serializable {
private final double re, im;

@AllArgsConstructor
private static class SerializedForm implements Externalizable {
private double re, im;

@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeDouble(re);
out.writeDouble(im);
}

@Override
public void readExternal(ObjectInput in) throws IOException  {
re = in.readDouble();
im = in.readDouble();
}

private Object readResolve() {
return new ImmutableComplex(re, im);
}
}

private Object writeReplace() {
return new SerializedForm(re, im);
}
}

I've left the standard lombok annotation unresolved, as it's already long enough.

Here, `SerializedForm` gets the two `Externalizable` methods. For a class with no final fields this is enough.

Otherwise, a mutable version of the class must be generated and the two conversion methods written. In Guava there're quite a few examples for this.


Maaartin G

unread,
Jul 8, 2012, 12:41:32 PM7/8/12
to project...@googlegroups.com

Tumi

unread,
Jul 8, 2012, 4:08:08 PM7/8/12
to project...@googlegroups.com
:oO

Reinier Zwitserloot

unread,
Jul 10, 2012, 5:28:33 PM7/10/12
to project...@googlegroups.com
Wow, good catch. This is starting to be ripe for the 'weeeelll, you don't do this often, but if you need it, boy that's a lot of boilerplate you can toss right out'.

This does involve generating entire inner classes which so far we've never done. Those need to have fields which we still haven't entirely nailed in eclipse.

NB: Yeah, we're starting to sound like a broken record on the fields thing. We should probably figure out exactly why it's so finnicky and fix it :)

Fabrizio Giudici

unread,
Jul 10, 2012, 5:38:05 PM7/10/12
to project...@googlegroups.com
On Tue, 10 Jul 2012 23:28:33 +0200, Reinier Zwitserloot
<rein...@gmail.com> wrote:

> Wow, good catch. This is starting to be ripe for the 'weeeelll, you don't
> do this often, but if you need it, boy that's a lot of boilerplate you
> can
> toss right out'.

Why SerializedForm can't just be a Serializable, thus saving writeExternal
and readExternal? After all its fields aren't final and AFAIK
readResolve() can be used with Serializable too.

Maaartin G

unread,
Jul 11, 2012, 3:12:14 AM7/11/12
to project...@googlegroups.com
On Tuesday, July 10, 2012 11:38:05 PM UTC+2, Fabrizio Giudici wrote:
On Tue, 10 Jul 2012 23:28:33 +0200, Reinier Zwitserloot  

> Wow, good catch. This is starting to be ripe for the 'weeeelll, you don't
> do this often, but if you need it, boy that's a lot of boilerplate you  
> can
> toss right out'.

Why SerializedForm can't just be a Serializable, thus saving writeExternal  
and readExternal? After all its fields aren't final and AFAIK  
readResolve() can be used with Serializable too.

There are two problems to be solved:
1. Speed, which needs Externalizable (see the post by Tumi).
2. Externalizable with final fields, which needs the SerializedForm.

Using Serializable with final fields is possible directly (due to some Unsafe magic). Then, using SerializedForm still makes sense, as it allows the compatibility of the stored data even when the implementation changes a lot (note that after such a change, it'd be necessary to re-write it manually). So there are two problems to be solved and both of them make sense alone, but a solution for both of them together makes much more sense.

Fabrizio Giudici

unread,
Jul 11, 2012, 3:37:34 AM7/11/12
to project...@googlegroups.com, Maaartin G
It's not what I was saying: in the code example I was referring to the
part where there is an Externalizable with *non final* variables and it's
for that part that I was referring to Serializable. To be clear, my
suggestion is the following, could this work?

@RequiredArgsConstructor @Getter
public class ImmutableComplex implements Serializable {
private final double re, im;

@AllArgsConstructor
private static class SerializedForm implements SERIALIZABLE {
private double re, im;

private Object readResolve() {
return new ImmutableComplex(re, im);
}
}

private Object writeReplace() {
return new SerializedForm(re, im);
}
}


I missed the speed problem, but it seems an orthogonal problem and perhaps
could be selected by the programmer with an annotation attribute. After
all a lot of people uses plain Serializable with no worries about speed,
but would just love to use final fields.

Tumi

unread,
Jul 11, 2012, 9:27:59 AM7/11/12
to project...@googlegroups.com, Maaartin G
Hi,

Here are my thoughts / design suggestion at least for a initial implementation:

  1. I do believe serialization performance matters, a lot. But many times the developer doesn't actually realize the problem. For instance, I have heard many times "JMS performance sucks!", when the actual problem is the default serializatin mechanism of my custom classes.
  2. Serialization of raw Serializable classes is much slower than serialization of Externalizable classes (if good implemented, of course). But other serialization frameworks are even much better. I really love Kryo, by the way (https://code.google.com/p/kryo/) which does a very smart work (for instance, writing only the milliseconds (a long) when serializing a Date, etc)
  3. I think serialization and clonation go togeter on the hand. So @Serializable/@Externalizable and @Clone maybe could be addressed at the same time.
  4. The default serialization for not Externalizables doesn't suffer from the "final fields problem" (which actually is "no-noArgsConstructor problem") due to the Unsafe ot whatever magic.
  5. Neither the default-and-slooow JDK cloning mechanism (which relies on serialization, I think) does't have that problem.
  6. I like the freedom to choose the proper logging framework in @Log, maybe this can be done here to choose implementing a "default" externalization, or via Kryo, or via other frameworks.
  7. [proper handling of Clone should decide / let choose if it makes a "shallow copy" or a "deep copy" as discussed in some relevant threads here... ]
So a first approach could be different if the annotated class has a noArgs-constructor than if it doesn't. An speed-optimized approach for the first case, and a default approach for the second.

For example, the code injected for a class with final fields could be (I will leave aside the Clone stuff, but I insist that both should (could) be handled together:

(a) If no noArgs-constructor found (different than the default constructor, so warning: @CloneAndSerialize should be handled after the ConstructorHandler!):

@Data @Serialize
public class ImmutableComplex  {
private final double re, im;
}

looks like (this variant generates a not optimized (but working!) version:

@Data
public class ImmutableComplex implements Serializable {
private final double re, mi;
}

(b) If a noArgs-constructor is found:

@Data @Serialize
public class Mutable {
private double re, mi;
}

looks like (optimized version!):

@Data
public class Mutable implements Externalizable {
private final double re, mi;

@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeDouble(re);
out.writeDouble(mi);
}

@Override
public void readExternal(ObjectInput in) throws IOException  {
re = in.readDouble();
mi = in.readDouble();
}
}


What do you think?

Tumi

unread,
Jul 11, 2012, 9:30:29 AM7/11/12
to project...@googlegroups.com, Maaartin G
By the way: I have created a proof of concept for this duality (both for Clone and Serializable/Externalizable) in a custom Lombok extension.... It is working (still test-casing) and fits my concrete library needs, in which I implement Externalizable and Cloneable by delegating into Kryo!!). Attached.

But I have been only able to do it for the javac handler :-( 

Ok, I know this wouldn't be a solution for the main Lombok library, but it can be an idea for a possible future Kryo-dependant version, and may help other newbies like me, and also I would be great if some kind soul helps me to "translate" this stuff into a Eclipse handler... or any suggestion... :-)

Cheers and thanks!
CloneAndSerializeHandler.java

Tumi

unread,
Jul 11, 2012, 9:37:02 AM7/11/12
to project...@googlegroups.com, Maaartin G
Glups, copy-paste error. In the last snippet I meant, of course:


@Data
public class Mutable implements Externalizable {
//private final double re, mi;
private double re, mi;
...




On Wednesday, July 11, 2012 3:27:59 PM UTC+2, Tumi wrote:

Fabrizio Giudici

unread,
Jul 11, 2012, 12:28:26 PM7/11/12
to project...@googlegroups.com, Tumi, Maaartin G
On Wed, 11 Jul 2012 15:37:02 +0200, Tumi <serverpe...@gmail.com>
wrote:

> Glups, copy-paste error. In the last snippet I meant, of course:
>
> @Data
> public class Mutable implements *Externalizable* {
> //private final double re, mi;
> private double re, mi;
> ...

Well perhaps there are many developers complaining about the poor
performance of JMS not knowing it's the serialization, but there are also
a lot of people that happily live with the default serialization. It
depends on the context and I have never suffered major performance
problems. In this case, using just another serialization framework is an
increment of entropy that would for sure deter many from using Lombok for
serialization.

But of course your points are valid and I'd like to add that I'd love to
see annotation based serialization in XML or JSON. So, I think we should
have a mechanism that by default just uses the Java serialization, but can
use different approaches. Note my point: I'd like to choose the approach
*at runtime*, not at compilation time, because I could reuse the same
class in different context with different needs. In my code I usually
don't have serialization implemented in classes owning the data, but in
external services that can be plugged.

Maaartin G

unread,
Jul 11, 2012, 11:49:51 PM7/11/12
to project...@googlegroups.com, Maaartin G
On Wednesday, July 11, 2012 9:37:34 AM UTC+2, Fabrizio Giudici wrote:
It's not what I was saying: in the code example I was referring to the  
part where there is an Externalizable with *non final* variables and it's  
for that part that I was referring to Serializable. To be clear, my  
suggestion is the following, could this work?

AFAIK yes. But it makes sense only iff you want to separate the serial form from the implementation, so you could one day change the implementation and stay compatible. Otherwise you can simply let Serializable do the Unsafe magic. Or maybe there's some speed difference... I don't know.

I'm not sure if this is useful. AFAIK, the only disadvantage of Externalizable with the SerializedForm form is the crazy amount of code it needs. With lombok it's gone, so what do we need plain Serializable for?

Maaartin G

unread,
Jul 11, 2012, 11:56:34 PM7/11/12
to project...@googlegroups.com, Maaartin G
On Wednesday, July 11, 2012 3:27:59 PM UTC+2, Tumi wrote:
6. I like the freedom to choose the proper logging framework in @Log, maybe this can be done here to choose implementing a "default" externalization, or via Kryo, or via other frameworks.

I'm afraid, it's gonna get too complicated very soon as they're probably big differences between the frameworks. With logging it's much easier as there's just a single and trivial line to be inserted. Here, different frameworks may have same methods with different names or argument order, they may do some things very differently and some others maybe not at all.

IMHO, lombok's serialization would work best with lombok's constructors. With @AllArgsConstructor or @RequiredArgsConstructor the handling of final fields gets trivial.
 
... I will leave aside the Clone stuff, but I insist that both should (could) be handled together:

Maybe. The basic operations for cloning and serialization are about the same.
The poor man's deepClone can be implemented in a few lines using serialization (it's sure to be slow and it may blow up, if anything is not Serializable).

However, cloning has some specifics, as already discussed:
  • there's no point in cloning immutables
  • there are different cloning method names in use (and also cloning constructors, etc.)
  • IMHO, the most important cloning thing is one never mentioned anywhere: level-1 clone

For example, for cloning

class C {byte[] b; String name; Set<String> aliases;}

you want to do `b.clone()`, do nothing to name (as it's immutable), and clone `aliases`. The last part is tricky as there's no general method for this:
  • Your set may be a `HashSet` and you need to invoke `clone()`.
  • It may be an `ImmutableSet` and you need nothing at all.
  • It may be a `Collections.SynchronizedSet` and you have to look at the underlying set.
  • Or a `HashMap.KeySet` and what you do?
  • Or a completely unknown set and you can do nothing at all.
I don't see how this could be done in lombok without some kind of API where the user could plug in their cloning helpers.

Fabrizio Giudici

unread,
Jul 12, 2012, 3:03:07 AM7/12/12
to project...@googlegroups.com, Maaartin G
On Thu, 12 Jul 2012 05:49:51 +0200, Maaartin G <graj...@seznam.cz> wrote:

> needs. With lombok it's gone, so what do we need plain Serializable for?

Safety checks? I don't recall, does Serializable check for serialVersionID?

Maaartin G

unread,
Jul 12, 2012, 7:11:39 PM7/12/12
to project...@googlegroups.com, Maaartin G
On Thursday, July 12, 2012 9:03:07 AM UTC+2, Fabrizio Giudici wrote:
On Thu, 12 Jul 2012 05:49:51 +0200, Maaartin G wrote:

> needs. With lombok it's gone, so what do we need plain Serializable for?

Safety checks? I don't recall, does Serializable check for serialVersionID?

The serialVersionID is a static field created to check if the class was changed in an incompatible way, while @Externalizable changes only the way the instance fields get written. So I'd guess that the checks get applied to @Externalizable as well.

I wasn't very sure and tried it out:

java.io.InvalidClassException: maaartin.WW$X; local class incompatible: stream classdesc serialVersionUID = 8347786893055136811, local class serialVersionUID = 2463321486298882757

Reply all
Reply to author
Forward
0 new messages