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

Reply via email to