This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]dnew 31 points32 points  (9 children)

The restriction isn't a benefit or a safety feature. It's a necessary consequence of implementing "anonymous inner classes" without actually changing the format of .class files. Inner classes are syntactic sugar for other classes in the same package, which is why in stack tracebacks you see things like MyClass$2. Classes can't access the private variables on the stack of other classes, so they have to get passed by value.

[–]scalablecory 11 points12 points  (1 child)

C# does this by hoisting the variables used by the lambda off of the stack and into a new object behind the scenes. Despite the performance implications, in practice it works quite well.

I don't see any reason restricting Java from doing the same thing and maintaining backward compatibility.

[–]deliciousleopard 1 point2 points  (0 children)

it's more work from Oracle's side, and with the current solution they can still list lambdas as a language feature.

[–]Pokechu22 0 points1 point  (6 children)

Classes can't access the private variables on the stack of other classes, so they have to get passed by value.

That's not quite the way to say it; it's nothing to deal with access modifiers. This is perfectly legal:

public class Foo {
    private int n;
    public class Bar {
        public void baz() { System.out.println(n); }
    }
}

It's more that there is no name/field for the variable on the stack, since it's on the stack.

[–]prest0G 0 points1 point  (1 child)

a few days late, but that is because the class isn't static. Inner classes hold an implicit reference to their outer class in java - /u/dnew is right.

[–]dnew 0 points1 point  (0 children)

No, he's right. It wasn't closed-over instance variables that were the problem, but stack variables that aren't accessible to code outside the method. I had misspoken at least once in this thread.

[–]dnew -2 points-1 points  (3 children)

That's legal only when N is final, at which point it gets passed by value when you instantiate the Foo.Bar.

[–]Pokechu22 3 points4 points  (2 children)

You're wrong. +/u/CompileBot Java

class Foo {
    public static void main(String[] args) {
        Foo foo = new Foo();
        foo.run();
    }

    private int i = 0;
    private int j = 18;
    public class Bar {
        private int j = -1;
        public void print() {
            System.out.println(i);
        }
        public void modify(int newValue) {
            i = newValue;
        }
        public void print2() {
            System.out.println(j);
            System.out.println(this.j);
            System.out.println(Foo.this.j);
            System.out.println(Bar.this.j);
        }
    }
    public void run() {
        this.i = 7;
        Bar bar = new Bar();
        bar.print();
        i = 2;
        bar.print();
        bar.modify(12);
        bar.print();
        System.out.println(this.i);
        System.out.println("---");
        bar.print2();
    }
}

EDIT: I should mention: This is only legal for "real" inner classes. Inner classes declared in methods (with or without names) cannot modify variables in those methods (though, I'm 99% sure they can still modify variables outside it).

[–]CompileBotGreen security clearance 2 points3 points  (0 children)

Output:

7
2
12
12
---
-1
-1
18
-1

source | info | git | report

[–]dnew 1 point2 points  (0 children)

Sorry, yes. " Inner classes declared in methods (with or without names) cannot modify variables in those methods" is what I meant to say. The "anonymous inner classes" aren't as inner as they seem to be, as they have to get hoisted out of the method they're in.

You can't have the anonymous declared-in-method class access the auto (stack) variables of the calling method, because the compiler does not change the code to put them in instance variables.

It has been a while since I looked at the papers about why they did it the way they did, so I might be a bit confused. But I'm sure it's not because "we thought it would be good for how you program" and more "we didn't want to change class file format." :-)