1

Do local inner classes (which I believe includes anonymous classes) maintain a copy of all variables defined in the scope of the method that these local inner classes are defined in?

It would be because if the local inner classes were using the local variables and the method went out of scope while the inner class object was still alive, the object would be using something which no longer exists.

But is this statement true? Is it mentioned somewhere in official docs?

This is a follow-up question of this answer. I left a comment there but did not get a response, and I figured this is a different question as well, so asking here.

Community
  • 1
  • 1
Solace
  • 8,612
  • 22
  • 95
  • 183
  • 1
    It's an implementation detail. – Oliver Charlesworth May 22 '16 at 16:03
  • @OliverCharlesworth One would need to know in order to make an educated guess about why is a local inner class not allowed to access local variables in the method it is defined in, _**unless those variables are `final`**_ - i.e. how does `final` help the scenerio (mentioned in question) of the local variable going out of scope while the inner class object is still alive. – Solace May 22 '16 at 16:10
  • Isn't that already answered by the answers to the linked question? – Oliver Charlesworth May 22 '16 at 16:12
  • @OliverCharlesworth The answer there is based on the claim that the local inner class maintains a separate copy of the local variables - but there are varying opinions on this one. For example, see the answer below. – Solace May 22 '16 at 16:15
  • 1
    Because it depends on whether you're talking about semantics or implementation. The language standard is concerned with semantics, the compiler is concerned with implementation. – Oliver Charlesworth May 22 '16 at 16:16
  • @OliverCharlesworth But the 'language standard' made the rule that local inner classes CAN access local variables which are `final`. I wanted to know why... – Solace May 22 '16 at 16:52

4 Answers4

4

No, local inner classes don't "keep a copy", but they can reference final or, since , effectively final local variables in the method.

More insidiously, they keep a reference to the instance, which can cause memory leaks.

Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • "they keep a reference to the instance" - instance of what? – Solace May 22 '16 at 16:13
  • 1
    The instance of the object whose method they were created in. – Bohemian May 22 '16 at 16:14
  • What if they were created in a static method? – Solace May 22 '16 at 16:15
  • 1
    Still holds true. The instance then is the class object. – Bohemian May 22 '16 at 16:16
  • @Bohemian - why would you need a reference to a class object? The compiler knows what class it was defined in. – Oliver Charlesworth May 22 '16 at 16:18
  • 1
    @Oliver they don't, I just reasoned it as an edge case of the general statement I made that "they keep a reference to the instance of the object whose method they were defined in". They can access `private static` fields etc. – Bohemian May 22 '16 at 16:22
  • It started when I learnt that local inner classes can't(or couldn't in pre Java-8) access local variables. The question was WHY and the answer was the scenerio (mentioned in question) in which a local variable goes out of scope after the method returns, while an object of the local inner class is still alive; and you are left with an object of the inner class trying to access something which does not exist. – Solace May 22 '16 at 16:27
  • Then the next piece of information was, "BUT local inner classes CAN access local variables which are `final`". So the question was how does `final` help the mentioned problem with local variables going out of scope. I found the answer I linked in the question, it made sense (only if it were true as well!) that the local inner class gets a copy of the local variables in its scope, and what if the local inner class change the value of the copy it gets, the developer would expect... continued in next comment – Solace May 22 '16 at 16:33
  • the change in the copy located in the method ([touched in this answer as well](http://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class#answer-4732617)) ... which would cause confusion. **That's why they made this `final` rule.** So yeah, can you comment on this theory? I mean how does what you said (that inner class will get an instance) relate to the `final` rule? – Solace May 22 '16 at 16:35
  • 1
    @Solace the inner class getting an implicit reference to the instance does't relate to it being able to reference (effectively) final local variables. As a compiler implementation detail, they may be "given a copy" of the variable. – Bohemian May 22 '16 at 16:43
2

Do local inner classes maintain a copy of all local variables in the scope that they are defined in?

"Copy" is an ambiguous term.
But some sort of copying has to take place in order to deal with the two scopes that will each go their own way after instantiation.

First of all, local inner classes can only refer to variables of enclosing scopes if those are "final or effectively final", as javac calls it.
Trying to use a variable whose value changes after a local inner class declaration will produce an error message like:

Test.java:13: error: local variables referenced from an inner class must be final or effectively final
                System.out.println(i);
                               ^

Since we therefore only have final variables, primitive values can be copied without any headache.

As for objects, since they are actually pointers in Java, we only copy the pointer and not the entire object, just like in any other object variable assignment.
Doing so creates an additional "reference" to the object, which will prevent the garbage collector from cleaning it up once the original variable goes out of scope, as long as our inner class is still running.

Is it mentioned somewhere in official docs?

I don't believe so. The only thing I could find was in §8.1.3 of the Java Language Specification, which is pretty vague:

When an inner class (whose declaration does not occur in a static context) refers to an instance variable that is a member of a lexically enclosing class, the variable of the corresponding lexically enclosing instance is used.

(And I'm not sure whether a declaration inside a static method meets the definition of "static context", or not.)

You can, however, inspect how javac deals with this, by disassembling a .class file using javap -c -private ....

(Assuming Java 8 from here on)
Given the following code:

class Test
{
    public static void main(String[] args)
    {
        int i = 42;
        String s = "abc";
        Object o = new Object();
        java.io.InputStream in = null;
        Runnable r = new Runnable()
        {
            public void run()
            {
                System.out.println(i);
                System.out.println(s);
                System.out.println(o);
                System.out.println(in);
            }
        };
    }
}

So we have an int, a String, an Object and an InputStream in the outer scope, referenced to from the inner scope.

A call to javac Main.java creates two files: Test.class and Test$1.class.
Test.class isn't too interesting, but Test$1.class reveals how exactly those variables are stored and accessed later:

bash$ javap -c -private 'Test$1.class'
Compiled from "Test.java"
final class Test$1 implements java.lang.Runnable {
  final int val$i;

  final java.lang.String val$s;

  final java.lang.Object val$o;

  final java.io.InputStream val$in;

  Test$1(int, java.lang.String, java.lang.Object, java.io.InputStream);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #1                  // Field val$i:I
       5: aload_0
       6: aload_2
       7: putfield      #2                  // Field val$s:Ljava/lang/String;
      10: aload_0
      11: aload_3
      12: putfield      #3                  // Field val$o:Ljava/lang/Object;
      15: aload_0
      16: aload         4
      18: putfield      #4                  // Field val$in:Ljava/io/InputStream;
      21: aload_0
      22: invokespecial #5                  // Method java/lang/Object."<init>":()V
      25: return

  public void run();
    Code:
       0: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0
       4: getfield      #1                  // Field val$i:I
       7: invokevirtual #7                  // Method java/io/PrintStream.println:(I)V
      10: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      13: aload_0
      14: getfield      #2                  // Field val$s:Ljava/lang/String;
      17: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      20: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      23: aload_0
      24: getfield      #3                  // Field val$o:Ljava/lang/Object;
      27: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      30: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      33: aload_0
      34: getfield      #4                  // Field val$in:Ljava/io/InputStream;
      37: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      40: return
}

So basically javac just creates a final field and a corresponding constructor parameter in the inner class for each referenced variable of the outer scope.

I don't know how other compilers do this, but

  • I haven't found a specification for this.
  • You can inspect how each of them does it, using your favourite Java disassembler.
Siguza
  • 21,155
  • 6
  • 52
  • 89
1

Just for clarity

An inner (non-static) class object has access to the outer classes this. On object has some implicit fields, this, Outer.this, OuterOuter.this as far as the nesting goes.

class Outer {

    int outerField;

    class Inner {
         String innerField;
         ... innerField ... outerField
         ... this.innerField
         ... Outer.this
    }

    ... new Inner();
}

Outer outer = new Outer();
Inner inner = outer.new Inner();

Advantage: Inner classes allow to create a container class providing items, and every item has access to the container using the outer-this field.

Disadvantage: if one serializes such an inner class object to file, it also serializes the outer object, so the behavior can be weird.

(As such the answers should be obvious.)

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

There are a couple of things going on here:

  • The JLS (Java Language Standard) is concerned with semantics. Semantically, there is no copy going on (or rather, the effect of a copy would be indistinguishable). So it says nothing about it.
  • The compiler is concerned with implementation. Creating an under-the-hood copy of variables is a very easy way to implement the semantics of local-variable capture (given non-mutation).
  • The authors of the JLS were of course aware of the above point, so almost certainly curated the semantics (in particular, the constraints around final/effectively-final) accordingly.

So to revisit your title question:

Do local inner classes maintain a copy of all local variables in the scope that they are defined in?

The answer is that the JLS says nothing about it, and in a practical compiler implementation it will probably only be a copy of the final/effectively-final variables.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • Since the question contains the phrase “*all* variables defined in the scope …”, it’s worth emphasizing that instances of local classes only contain copies of variables that are actually referenced in the code of that class. Since only references to (effectively) final variables are allowed, that precludes copies of mutable variables per se. The only exception is the `this` reference which is always kept, even if unused. – Holger Jun 02 '16 at 18:18