final transient fields serialization

David M. Lloyd david.lloyd at redhat.com
Mon Nov 9 23:38:59 UTC 2009


On 11/09/2009 03:30 PM, Pawel Veselov wrote:
> Hi,
>
> it again caught my attention, and I though that may be there is
> something that can be done about this.
> The issue is obvious -- having 'final transient' instance fields makes
> little sense if the object is ever serialized.
> Logically, there may be perfect reasoning behind making an instance
> field final, as well as transient, in which case there is then no
> mechanism to reinitialize this field on object deserialization.

I've used final transient fields before to hold values which are not 
relevant on the remote side (and thus can be null or 0), but point taken...

> It seems that it would be nice if either the final fields were
> initialized in a separate block that would be executed on
> deserialization, or if readObject() could set them. After all you can
> have a code block that sets the final fields. Not sure how feasible that
> is, but IMHO, that is a short coming.

One possible problem with this is that changing a final field might have 
some JMM implications (case in point, CopyOnWriteArrayList uses 
sun.misc.Unsafe#putObjectVolatile() to reinitialize the transient final 
Lock field, though I don't see anywhere that ObjectInputStream itself takes 
such precautions; one would think that java.lang.reflect.Field would take 
care of this for you, but perhaps it does not).

A workaround for this shortcoming is as follows.  Instead of using a 
transient final field with a custom readObject(), use a regular final field 
and add a writeObject() method which uses PutFields to change its value to 
a marker object.  The marker object should have a protected readResolve() 
method which constructs the correct value for the field on the remote side. 
  Using this trick might have odd results though.  For example, you can 
cause two objects to have final references to each other, which is 
ordinarily not possible without reflection.

Alternately, in some cases the actual new value might be computable on the 
writing side.  In either case there may be a bandwidth penalty however.

You could also use writeReplace() to replace the entire object with a 
minimal marker, which in turn does readResolve() on the remote side to 
construct the proper instance (using a constructor, thus it would be able 
to initialize all fields).  This approach fails in certain cyclic cases 
though, because the readResolve() isn't executed until after the entire 
object was deserialized, so any backreferences to the replaced object will 
be filled in with the marker object until then.

- DML



More information about the core-libs-dev mailing list