One final stab at improving lambda serialization

David M. Lloyd david.lloyd at
Tue Aug 27 14:01:35 PDT 2013

On 08/19/2013 10:37 AM, Brian Goetz wrote:
 > [...]
> The other failure has to do with order-of-capture.
> *Old code**
> * 	*New code**
> * 	*Result**
> *
> String s1 = "foo";
> String s2 = "bar";
> Runnable r = new Runnable() {
>      void run() {
> foo(s1, s2);
>      }
> };
> 	String s1 = "foo";
> String s2 = "bar";
> Runnable r = new Runnable() {
>      void run() {
>          String s = s2;
> foo(s1, s);
>      }
> };
> 	On deserialization, s1 and s2 are effectively swapped.
> This fails because the order of arguments in the implicitly generated
> constructor of the inner class changes due to the order in which the
> compiler encounters captured variables.  If the reordered variables were
> of different types, this would cause a type-1 failure, but if they are
> the same type, it causes a type-2 failure.

This appears to not strictly be accurate.  In my tests I've found that 
yes the constructor parameter ordering may indeed differ (based on this 
or (potentially) even any other compiler-specific factor), however since 
the constructor is not actually used during deserialize (the mandatory 
no-arg constructor of the first non-serializable superclass is used 
instead), this difference is irrelevant (to this particular problem). 
The serialization values are stored by field name in the serialization 
stream and filled in via reflection; every Java compiler I have access 
to names these fields e.g. "val$s1" and "val$s2" with the local variable 
name stored in the field name.  From my testing, this case seems like it 
would work "as expected" (and, given the apparent complete ubiquity of 
compiler implementation, probably ought to just be formalized in at 
least the serialization spec by now, if not the JLS itself, but that's 
really a tangential topic).

Also worth noting is that the outer class instance lives in a field 
called "this$0".

Anyway because this has always "just worked", I think that the 
definition of "significant [change]" used later on in your proposal 
should probably be changed to removing the "order" (and even "number") 
of captured arguments; the former is handled as above, and the latter 
works predictably by simply extrapolating the serialization spec rules 
about adding/removing fields to captured variables.  This is the basis 
of my earlier gripes about only using capture order (i.e. an array, 
essentially) for captures instead of names (i.e. a map like 
serialization does).

More upcoming...

More information about the lambda-spec-experts mailing list