Effectively final effective?

Artur Biesiadowski abies at adres.pl
Thu Feb 25 12:38:38 PST 2010


Reinier Zwitserloot wrote:
> And yet that's what you are proposing: To avoid a simple, obvious, and 
> direct error message ("this variable is implicitly final because it is 
> accessed from a closure; if you'd like to mutate it, use 
> AtomicReference"), you've set up a situation where you implicitly 
> create a copy of a variable, and changing the one does not reflect 
> into the other. Horrible, _horrible_ idea.

I'm suggesting making it effectively final in the scope of 
lambda/anonymous class, just not outside of it. As far as bugs or 
understanding the concept is concerned, I do not see any fundamental 
difference between

int i;
for (i =0; i < 10; i++ ) {
  list.add(new Integer(i)); 
}
print(list);

and

int i;
for (i =0; i < 10; i++ ) {
  list.add(new Object() { public String toString() { return 
Integer.toString(i);}}); 
}
print(list);

In both cases, value of the variable is captured at the moment it 
appears in source code. Same way as you will not expect to see ten of 
'10' in first case, you won't expect to see ten '10' in second case. 
Still, you are arguing that while first case is obvious and will not 
cause bugs, second case HAS to be written

int i;
for (i =0; i < 10; i++ ) {
  final int j = i;
  list.add(new Object() { public String toString() { return 
Integer.toString(j);}}); 
}
print(list);

(with final possibly being removed if I understand your position 
correctly). Can you imagine explaining to newcomers in java (after final 
is removed) that they have to do int j =i; in the loop to pass the 
variable inside, because if they would NOT reassign it to different 
name, they would make a mistake of expecting 10x10 as the output? 
Example with loop is one of the main cases where I'm really angry about 
current rules (as I use final modifier for the most of normal, non-loop 
variables anyway) - of course, in real life it is bit more complicated :)

I think that having similar rules for method arguments and variables 
passed into lambdas/anonymous classes is not bad thing (after all, in 
both cases you are doing a method/function call, just code is inlined in 
case of lambda/anonymous class as opposed to normal method call).

Given something like @Shared would be accepted, all attempts to modify 
variable from inside closed scope would result in warning (make it 
shared or use in read mode only), but would not impose limits on the 
outside. Going further this route, to achieve symmetry between the 
inlined methods and normal methods, I could even imagine this evolving 
into by-reference passing for java. You would declare method

void swap(@Shared int a, @Shared int b);

and then you could pass variables declared as @Shared to swap, knowing 
that they will be mutated inside your scope. If passed into non-shared 
receiver, current value would be used. I would probably disallow 
automagical 'sharing' of normal variable if it is used as argument to 
method expecting @Shared - it should be compilation error (as @Shared 
will be most probably just a syntax sugar around AtomicXYZ or WrappedXYZ).

Anyway, I'm not advocating particular solution for @Shared here - just 
showing that there is a certain symmetry between method calls and lambda 
instantiations and I don't think it is unreasonable to expect closing 
over CURRENT value of variable being natural.

Regards,
Artur Biesiadowski


More information about the lambda-dev mailing list