Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does referencing an anonymous inner class by its name work when it's a member, but not a variable?

Tags:

java

Sorry for the title gore, I did not know how to describe the problem in one line. If you have suggestions, I'm open.

Suppose you have the following class:

public class SomeClass {
    // doesn't even need to be final, which is freaky
    Runnable memberRunnable = new Runnable() {
        public void run() {
            SomeOtherClass.someMethod(memberRunnable); // this works
        }
    }
    public void someMethod() {
        final Runnable varRunnable = new Runnable() {
            public void run() {
                SomeOtherClass.someMethod(varRunnable); // compiler error - "varRunnable" might not have been initialized
            }
        }
    }
}

Why is the memberRunnable able to access itself from inside run(), while varRunnable is not? AFAICS it's the exact same construct.

You can obviously use this instead, I know. I'm just wondering why the compiler makes a difference between the two cases, which seem identical. Also why it thinks varRunnable might not have been initialized, when it's obvious that it is at that point.

One could argue that, if Runnable was a class (it's an interface), it's constructor might be trying to call run(), thus actually running into a scenario where the reference is uninitialized. However, this should also be the case for memberRunnable, but that case works.

Funny thing, nothing changes if instead of Runnable you use a class, in which case the above scenario (constructor calling an overridden method) can actually happen. This means that, in that case, you can run into a "field not initialized" at runtime (haven't tried it though), which is rather dumb since the compiler should guard against that.

like image 956
Felix Avatar asked May 08 '15 17:05

Felix


People also ask

Why would you use an anonymous class rather than an inner class?

Anonymous classes enable you to make your code more concise. They enable you to declare and instantiate a class at the same time. They are like local classes except that they do not have a name. Use them if you need to use a local class only once.

What is true about anonymous inner class?

It can extend exactly one class and can implement multiple interfaces. It can extend exactly one class and implement exactly one interface. It can implement multiple interfaces regardless of whether it also extends a class. It can extend exactly one class or implement exactly one interface.

Can an anonymous inner class be declared private?

Restriction on Anonymous Inner class Anonymous inner class cannot be declared as public, private, protected, or static. It cannot access local variables of its enclosing scope that are not declared as final or effectively final.


1 Answers

Also why it thinks varRunnable might not have been initialized, when it's obvious that it is at that point.

No, the variable is not (in the general case) guaranteed to be initialized at that point.

Suppose, just for the sake of argument, that Runnable was an abstract class (rather than an interface) and that the constructor of Runnable called this.run(). Since construction of the Runnable occurs before the assignment, this would result in access of varRunnable before the assignment had occurred.

In other words, it would lead to access of an uninitialized local variable. Note that this is worse than accessing a field that has not yet been explicitly initialized, since local variables are not initialized to default values. It's so much worse in fact, that access of uninitialized local variables is forbidden, while access to fields that have not been explicitly initialized is allowed, as you just discovered. (Making the field final doesn't change this. Final fields also have default values, and they can in fact change (once) in the constructor.)

Source: I'm a javac developer.

like image 160
aioobe Avatar answered Oct 24 '22 17:10

aioobe