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.

6
  • 1
    It's an implementation detail. Commented May 22, 2016 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. Commented May 22, 2016 at 16:10
  • Isn't that already answered by the answers to the linked question? Commented May 22, 2016 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. Commented May 22, 2016 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. Commented May 22, 2016 at 16:16

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

10 Comments

"they keep a reference to the instance" - instance of what?
The instance of the object whose method they were created in.
Still holds true. The instance then is the class object.
@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.
@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.
|
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.

2 Comments

@OliverCharlesworth How so? Im declaring them inside a method. How is that not method-local?
Apologies, you're absolutely correct - I misread your code snippet :(
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.

1 Comment

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.
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.)

1 Comment

The OP is asking about anonymous classes accessing method-scope variables.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.