Skip to main content

Ability to access non-final variables from inner classes

5 replies [Last post]
cowwoc
Offline
Joined: 2003-08-24

Hi,

I am partially aware of why inner classes can only interact with final variables but at the end of the day, developers simply resort to:

final int[] result = new int[1];
anonymousClass
{
write into result[0];
}

which is simply sad. I am proposing that the compiler handle this sort of thing on behalf of the user so from the user's point of view he is writing directly to "int result" while the compiler translates it into the above bytecode. It makes it easier to code in Java and it sounds simple to implement. What do you think?

Gili

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
yishai
Offline
Joined: 2003-11-16

> Hi,
>
> I am partially aware of why inner classes can only
> interact with final variables but at the end of the
> day, developers simply resort to:
>
> final int[] result = new int[1];
> anonymousClass
> {
> write into result[0];
> }
>
> which is simply sad. I am proposing that the compiler
> handle this sort of thing on behalf of the user so
> from the user's point of view he is writing directly
> to "int result" while the compiler translates it into
> the above bytecode. It makes it easier to code in
> Java and it sounds simple to implement. What do you
> think?
>
> Gili

I remember reading somewhere the motivation for forcing the local variables to be final was something like this:

[pre]
for (int i = 0 ; i < 10 ; i++) {
new Thread(new Runnable(){
public void run() {
System.out.println(i);
}
}).start();
}
[/pre]

What would you expect? The result would be 10 9s printed out, because by the time the thread started up i would have gotten to 9. (And that was the result before they implemented the restriction).

Now, admittedly you could force the same thing to happen with an array, but it would be more obvious.

For me, that was never a compelling argument, but on the other hand if that is what you need out of a thread, odds are your design should be better, and there should be some sort of dispatch object involved.

So my vote is 0.

zander
Offline
Joined: 2003-06-13

Your threading example is nice; but not the reason for this at all.
The reason is actually quite simple; scoping. When a variable goes out of scope it gets garbage collected. anon-inner classes can reference all variables of their parent, as long as the parent lives.
So if the parent is a method; the anon class will not be able to reference any variables that were declared in the scope of that method after the method is left.

The construct that final variables are visible to the inner class, even when the variable went out of scope for the method, solves this problem when inner classes live longer then the method where they were invoked.

yishai
Offline
Joined: 2003-11-16

> The construct that final variables are visible to the
> inner class, even when the variable went out of scope
> for the method, solves this problem when inner
> classes live longer then the method where they were
> invoked.

Scoping could definitely affect the parent request, but it isn't sufficient to explain all cases. For example:

[pre]
int i = 0;
i++;
new Thread() {
public void run () {
System.out.println(i);
}
}.start();
[/pre]

The compiler generates byte-code which makes a copy of i. There is no scoping reason why the above code couldn't work just as well as a final i. By forcing the finally, the language is forcing the copy to be invisible. It doesn't matter to you if/when it is copied, as the value is always the same.

Anyway, what I read before was from a Sun employee on Sun's site, but it was a while back, and I can't find it easily now.

The official JLS reason is syncronization (imagine modifying i after the thread starts, who wins on the modification, and i could be modified in surprizing ways).

Scoping is a good way to think of the problem, but it isn't an actual restriction on implementing it differently, provided you allowed for some shared scope between the two classes, or you just copied and let the programmer be surprized.

Anyway, to the actual suggestion, I was thinking about it more, and perhaps an implicit reference to a return variable (it would have to be an Object, but we have autoboxing, so that shouldn't be a problem) that would only exist with anonymous inner classes would help make the cases where the inner class returns something more codable. I'm thinking of something like this:

[pre]
Thread t = new Thread(){
public void run() {
doSomething();
return = 1; //autobox the 1, and the = next to the return tells you that it is not a return statement.
doSomethingElse(); //But I don't actually return. Or maybe we should say that you do and this would fail as an unreachable statement? Don't know.
}
}
t.start();
Thread.sleep(10000);
int i = t.return;
[/pre]

I guess there are two issues, one is the concept, and the other is the use of the return keyword. I only think of it because I would want to avoid another keyword.

patrikbeno
Offline
Joined: 2004-10-11

> Scoping could definitely affect the parent request,
> but it isn't sufficient to explain all cases. For
> example:
>
> [pre]
> int i = 0;
> i++;
> new Thread() {
> public void run () {
> System.out.println(i);
> }
> }.start();
> [/pre]
>
> The compiler generates byte-code which makes a copy
> of i. There is no scoping reason why the above code
> couldn't work just as well as a final i.

It is still the same reason - scoping. Inner thread may not have any idea when 'i' goes out of scope.

> The official JLS reason is syncronization (imagine
> modifying i after the thread starts, who wins on the
> modification, and i could be modified in surprizing
> ways).

I can't believe this. Synchronization is about threads, and there is no difference between the threads created from main() method and threads created as inner classes.

> Scoping is a good way to think of the problem, but it
> isn't an actual restriction on implementing it
> differently,

I believe it is. You end up with multiple independent instances where inner instances refer to data declared in the call stack of the method that created them.
Note that you can access outer class' instance variables without declaring them final:
[code]
public class Test {
int field;
static public void main(String[] args) {
final Test t = new Test();
final int stack = 1;
new Thread() {
public void run() {
System.out.println(t.field);
System.out.println(stack);
}
}.start();
}
}
[/code]

> about it more, and perhaps an implicit reference to a
> return variable (it would have to be an Object, but

Not an option. Ugly and does not solve multiple variables problem. Anyway, I don't understand how this is supposed to solve the problem. :-(

yishai
Offline
Joined: 2003-11-16

> It is still the same reason - scoping. Inner thread
> may not have any idea when 'i' goes out of scope.

It doesn't matter, as a copy is made anyway. When i is final it still goes out of scope.

> I can't believe this. Synchronization is about
> threads, and there is no difference between the
> threads created from main() method and threads
> created as inner classes.

I'm sorry, I just don't get what you are saying here. Who said there was a difference?

> I believe it is. You end up with multiple independent
> instances where inner instances refer to data
> declared in the call stack of the method that created
> them.
> Note that you can access outer class' instance
> variables without declaring them final:

Right, and the final doesn't solve the scope problem. It just allows for a seemless silent copy. What I am saying is that if you remove the finally restriction what you end up with is copies, not references, but it would be unclear what was in the copy, as the code could change the original after object instantiation.

> Not an option. Ugly and does not solve multiple
> variables problem. Anyway, I don't understand how
> this is supposed to solve the problem. :-(

You would solve the multiple variable problem by returning an array instead of a single object. I am solving the problem by restating it. I think the underlying reason why we look for the method variable to be non-final in some cases is because there is no way to communicate with an anonymous inner class. It has no type, so a variable referencing it can only reference it by its interfaces or base classes. If you want to reference something special about the anonymous inner class, you have to make a class definition (at least a local class definition) and then use that. This is not terrible at all, but can lead to some bloating of code where you have to make a whole class definition for the purpose of getting a result.