16

We know that only final local variables can be accessed in an anonymous class, and there is a good reason here: Why are only final variables accessible in anonymous class?.

However, I found that an anonymous class can still access a non-final variable if the variable is an member field of the enclosing class: How can I access enclosing class instance variables from inside the anonymous class?

I am confused. We ensure that only a final local variable can be accessed in anonymous class because we don't want that the variable should be out-of-sync between anonymous class and local function. The same reason should apply to the case if we try to access a non-final enclosing class member in anonymous class.

Why would it not be a concern?

Community
  • 1
  • 1
ctk2021
  • 361
  • 2
  • 12
  • `The same reason could apply to the case if we try to access non-final instance variable in anonymous class.`.... That's called accessing fields of an object, from its reference. Why should the origin of object or declaration of its class matter ? – S.D. Jun 11 '15 at 07:12
  • @user2499800: have you seen my answer ? where have you reached with this ? would appreciate if you reply ... :) – Yash Sampat May 07 '16 at 11:31

4 Answers4

8

In the case of a local variable, a copy of the variable is what the anonymous class instance receives. For this reason, the local variable has to be made final before it can be used within the anonymous class, so that its value may not change later.

In the case of a member field of the enclosing class, there is no copy. Rather, the anonymous class gets a reference to the enclosing class, and thereby it accesses any/all member fields and methods of the outer class. So even if the value of the field changes, the change is reflected in the anonymous class as well, because it is the same reference.

I am confused. We ensure that only a final local variable can be accessed in anonymous class because we don't want that the variable should be out-of-sync between anonymous class and local function. The same reason should apply to the case if we try to access a non-final enclosing class member in anonymous class.

As you see, that is not the case. The copying happens only for local variables, not for member fields of the enclosing class. The reason is, of course, that an anonymous class holds an implicit reference to the enclosing class, and through that reference it can access any/all member fields & methods of the outer class.

To quote the link below:

A member variable exists during the lifetime of the enclosing object, so it can be referenced by the inner class instance. A local variable, however, exists only during the method invocation, and is handled differently by the compiler, in that an implicit copy of it is generated as the member of the inner class. Without declaring the local variable final, one could change it, leading to subtle errors due to the inner class still referring to the original value of that variable.

References:

1. Why a non-final “local” variable cannot be used inside an inner class, and instead a non-final field of the enclosing class can?.

Community
  • 1
  • 1
Yash Sampat
  • 30,051
  • 12
  • 94
  • 120
  • You could improve your answer by explaining why it is possible to use the reference instead of the copy. Although it may be obvious. – theMfromA Jun 11 '15 at 07:05
  • "Rather, the anonymous class instance gets a reference to the actual member field." No. They only have a reference to the enclosing instance. – newacct Jun 11 '15 at 22:25
2

Non-Static / Inner classes have a reference to there enclosing instance. So they can implicitly refer to instance variables and methods.

If it is a Parameter, even the enclosing class doesn't know anything about it, because it is only accessible from the Method which has defined this variable.

Like Y.S. already pointed out:

In the case of a local variable, a copy of the variable is what the anonymous class instance gets

B. Kemmer
  • 1,517
  • 1
  • 14
  • 32
  • "non-static inner classes" is a tautology. If classes are static and declared inside another class, they're called "static member classes". If they're not static, they're called "inner classes". – Erwin Bolwidt Jun 11 '15 at 07:16
  • @ErwinBolwidt : Yep I forgot the slash. I actually meant to write Non-Static / Inner classes. Thanks for that hint. – B. Kemmer Jun 11 '15 at 07:18
1

It is more the other way around, a local variable, say x outside the anonymous instance lives as long as the method call. Its object reference is stored on the call stack; x == address on the stack containing an object reference. The x inside the the anonymous instance is a copy as its lifetime is different, longer or even shorter.

As there now are two variables, it was decided not to allow assignment to x (weirdly to implement) and require the variable to be "effectively final."

Access to an outer member, is because the anonymous instance is of an inner class also containing an OuterClass.this reference.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
1

Consider following example

class InnerSuper{
    void mInner(){}
}
class Outer{
    int aOuter=10;
    InnerSuper mOuter(){
        int aLocal=3999;
        class Inner extends InnerSuper{
            int aInner=20;
            void mInner(){
                System.out.println("a Inner : "+aInner);
                System.out.println("a local : "+aLocal);
            }
        }
        Inner iob=new Inner(); 
        return iob;
    }
}

class Demo{
    public static void main(String args[]){
        Outer ob=new Outer();
        InnerSuper iob=ob.mOuter(); 
        iob.mInner();
    }
}

This won't generate any error in Java 1.8 or above. But in previous version this generates an error asking you to explicitly declare the local variable accessed within the inner class as final. Because what the compiler does is it will keep a copy of the local variable accessed by the inner class so that the copy will exist even if the method/block ends and the local variable goes out of scope. It asks us to declare it final because if the variable changes its value dynamically later in the program after the local inner class or anonymous class is declared, the copy created by the compiler won't change to its new value and could cause problems within the inner class by not producing expected output. Hence it advises us to declare it explicitly final.

But in Java 1.8 it won't generate an error as the compiler declares the accessed local variable implicitly final. It is stated as follows in Java Docs

An anonymous class cannot access local variables in its enclosing scope that are not declared as final or effectively final.

Let me explain what is meant by effectively final. Consider the following altered version of the above program

class Outer{
    int aOuter=10;
    InnerSuper mOuter(){
        int aLocal=3999;
        class Inner extends InnerSuper{
            int aInner=20;
            void mInner(){
                System.out.println("a Inner : "+aInner);
                System.out.println("a local : "+aLocal);
            }
        }
        aLocal=4000;
        Inner iob=new Inner(); 
        return iob;
    }
}

Even in Java 1.8 this will produce an error. This is because aLocal is dynamically assigned within the program. This means the variable cannot be considered effectively final by the compiler. As from what I've understood the compiler declares the variables that aren't changed dynamically as final. This is called the variable is effectively final.

Hence it is recommended that you declare the local variables accessed by a Local inner class or an anonymous class explicitly final to avoid any errors.

  • I am beginner can you explain the first code I am unable to understand what is Innersuper mouter() as you didn't used – chandu_reddim Feb 14 '21 at 01:57
  • 1
    `mOuter` is a method of the `Outer` class which returns an instance of `InnerSuper` class. This method is executed in the main method. If that's what you need clarification for. – Missaka Iddamalgoda Feb 14 '21 at 11:46
  • sir, if I am not wrong Inner is a local inner class but I got to know that we are not able to pass the local inner class to others. but here u are able how it can be ? or am i learnt wrong that " local inner class cannot be passed or returned " . I got believe this that due to as local inner class belongs to block (method or if or for). please clarification on this sir. Yes sir. I want to ask about what is mouter(). What u said about mouter i got clarity about what is mOuter. – chandu_reddim Feb 14 '21 at 13:48
  • The reason for not being able to return or pass a local inner class is because no other classes outside of the local scope has access to the definition of class making it useless for others outside of the scope. Hence, the Java Compiler is written so that it doesn't allow to return a Local Inner/Anonymous Class. But in this instance what i have returned is a subclass instance of InnerSuper Class where this class definition is available to other classes here. Hope you have the idea of how Super/Parent Class can be used with Child Class instances. – Missaka Iddamalgoda Feb 15 '21 at 01:24
  • Yes, sir Thank you, sir I gone through each line vividly. I got clarification sir. – chandu_reddim Feb 15 '21 at 11:26