0

In all the articles, it is said that java is copy-by-value and the inner class cannot access and modify the instance variables of the outer class. But it can be done now, why? My jdk is 1.8.

public class InnerClass {
    private int counts = 0;

    public void test(int src) {
        int in = 100;
        new Thread(new Runnable() {
            private int a() {
                counts++;
                counts = src;
                counts = in;
                System.out.println("in a..." + counts);
                return counts;
            }

            @Override
            public void run() {
                int s = a();
                counts = s;
                System.out.println("in run, " + counts);
            }
        }).start();
    }

    public int getCounts() {
        return this.counts;
    }

    public static void main(String[] args) {
        InnerClass innerClass = new InnerClass();
        innerClass.test(99);
        try {
            Thread.sleep(1000);
        } catch (java.lang.InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        }
        System.out.println("in main," + innerClass.getCounts());

    }
}
kaya3
  • 47,440
  • 4
  • 68
  • 97
rockwe
  • 11
  • 4
  • 1
    *"In all the articles ..."* which articles? – kaya3 Dec 14 '19 at 04:00
  • https://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class/4732617#4732617, you can see this – rockwe Dec 14 '19 at 04:02
  • 3
    That question refers to local variables in a method, not instance variables in a class. They are very different scenarios, though syntactically they might appear to be similar. – kaya3 Dec 14 '19 at 04:03
  • 1
    @chrylis anonymous classes have the same restrictions with regards to local variables – Slaw Dec 14 '19 at 04:21
  • no one tell the truth – rockwe Dec 14 '19 at 04:45
  • 1
    "no one tell the truth". Everyone here has told you exactly what the rules are, as defined in the _Java Language Specification_. It's not clear where your misconception is rooted. Are you aware of the difference between **local variables** and **fields**, that they are not the same thing? – Slaw Dec 14 '19 at 04:48
  • yes ,local variables --> variables in method;fields --> class-level vaiables. Could you please write a piece of code to confirm your conclusion? – rockwe Dec 14 '19 at 04:53
  • I'm glad [you managed to find the answer](https://stackoverflow.com/questions/59332196/why-the-non-final-instance-variable-of-the-outer-class-can-be-accessed-and-updat#comment104863972_59332244). If you want a formal reference on the subject, see [§8.1.3](https://docs.oracle.com/javase/specs/jls/se13/html/jls-8.html#jls-8.1.3) of the _Java Language Specification_. – Slaw Dec 14 '19 at 05:49

2 Answers2

3

Misconception on your end: such anonymous inner classes can not accept local variables of the enclosing scope. But they can access fields of the enclosing class without a problem.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • @rockwe What do you mean "no"? Not one line of code in your anonymous class or the enclosing method is reassigning a **local variable**. The only rule is that **local variables** must be _final_ or _effectively final_ when declared outside but used inside an anonymous class or lambda expression—this says nothing about **fields**. – Slaw Dec 14 '19 at 04:23
  • public void test(int src) { int in = 100; new Thread(new Runnable() { int i = -1; private int a() { counts++; counts = src; counts = in; i = 2; return counts; } @Override public void run() { int s = a(); counts = s; System.out.println("in run, " + counts); } }).start(); } – rockwe Dec 14 '19 at 04:29
  • I cant add a comment with a complete code sample here – rockwe Dec 14 '19 at 04:30
  • @rockwe But you can [edit your question](https://stackoverflow.com/posts/59332196/edit). – Slaw Dec 14 '19 at 04:33
  • @Slaw,can't edit ,it show me no page found.It's weird – rockwe Dec 14 '19 at 04:36
  • 1
    The edit link that Slaw commented works for me. Try it again. Or click on the "edit" link that is immediately beneath your question. – Stephen C Dec 14 '19 at 05:04
  • @all, have tried several times and failed. I have got the answer now,thanks all firends! – rockwe Dec 14 '19 at 05:37
3

The articles you have read are about parameters and local variables in methods or constructors; not other kinds of variables, such as instance variables in your example. It is forbidden for an inner class to modify a parameter or local variable belonging to an enclosing method, but not for it to modify any other kind of variable.

The reason for this is that for a variable to remain accessible and modifiable by an object, the memory for that variable has to remain allocated for the lifetime of that object.

  • The enclosing instance of an inner class will remain in memory for the lifetime of the instance of the inner class, because the instance of the inner class maintains a reference to its enclosing instance, meaning the enclosing instance cannot be garbage-collected before the instance of the inner class. So it is safe for the inner class to read and write to that variable.

  • However, a parameter or a local variable is allocated on the stack, not on the heap; it is deallocated as soon as the method returns, which may be during the lifetime of the instance of the inner class. So it would be unsafe for the inner class to read or write to that variable after its memory has been deallocated and potentially allocated for a different variable.

This does raise the question of why the inner class is allowed to read (but not write to) a final or effectively-final parameter or local variable. The answer to this is that its value is copied to an automatically-generated field belonging to the inner class, and the inner class reads the value of that field. The field exists for the lifetime of the object, of course, because it is part of the object.

But that raises another question: why are only final or effectively-final variables copied to fields of the instance; why not allow mutable automatically-generated fields? The answer to that is that more than one instance of an inner class (perhaps more than one inner class) might try to access the same variable, and making copies for each instance would give unexpected behaviour, since changes made by one instance wouldn't be visible to another.

That could be solved by supporting proper closures, as languages like Javascript do, but it would require the Java compiler to synthesize a class to represent the closure of a method, which would add a lot of complexity to the language specification and the compiler. This complexity is unnecessary since you can always just use instance variables to achieve the same result.

kaya3
  • 47,440
  • 4
  • 68
  • 97
  • After reading your answer carefully, is the internal anonymous class declared inside the method allocated on the heap or on the stack? After the method exits, the anonymous class allocated inside the method may survive, right? – rockwe Dec 14 '19 at 05:10
  • I assume you mean where the instance of that class is allocated, not where the class itself is allocated; yes, the instance is allocated on the heap, and it may survive after the enclosing method has returned. – kaya3 Dec 14 '19 at 05:12
  • The hidden variable is internal to the *instance* of the anonymous class. It is part of that instance ... and therefore on the heap. – Stephen C Dec 14 '19 at 05:13