6

I'm still not satisfied with explanation regarding anonymous class and final field. There were tons of questions trying to explain obvious problem but I have not found answers for all my questions :-)

Suppose following code:

public void method(final int i, int j) {
    final int z = 6;
    final int x = j;
    int k = 5;
    new Runnable() {
        public void run() {
            System.out.print(i);
            System.out.print(x);
            System.out.print(z);
            System.out.print(k);
        }
    };
}
  1. It's not possible to compile this code because of "unfinal" k property.
  2. I understand that compiler can replace z property with the declared value during compile time.

When I've searched for solution how exactly can works i and x I found this answer which says:

The compiler can then just replace the use of lastPrice and price in the anonymous class with the values of the constants (at compile time, ofcourse), and you won't have the problem with accessing non-existent variables anymore

How it could work for fields i and x if they are parameters of the method? They are not known during compile time? This approach can work for z.

On the other hand, there is an explanation regarding stack issues:

This allows the Java compiler to "capture" the value of the variable at run-time and store a copy as a field in the inner class. Once the outer method has terminated and its stack frame has been removed, the original variable is gone but the inner class's private copy persists in the class's own memory

I would understand that anonymous class somehow copied all required content (fields) during it's creation. Missing final has obvious problem that if some code below anonymous class declaration would change the value, the execution used possible stale values.

But ok, this should solve the problem when anonymous class' method is executed out of scope of used properties.

But this approach should work even without final declaration as it just copies all fields.

Both approaches seem like independent for me. Speaking of which - and it could solve my questions - I have not found how work final method field. They are not removed from stack even if method gets finished? Seems like nonsense for me but it would explain lot of things :-)

What is the correct answer?

Community
  • 1
  • 1
Martin Podval
  • 1,097
  • 1
  • 7
  • 16

3 Answers3

8

Because of the need for copying the variables from the method to the anonymous class (as described), it was a language design decision to require that the variable copied be final. So assignment in either method or anonymous class would not give stale values, and the code would be more consistent.

But! In Java 8 this requirement is mitigated: final no longer is needed, if the variable is de facto final: assignments are disallowed after the variable is "copied" in the anonymous class.

This makes sense because of the many function notations. Otherwise a button's actionPerformed suddenly would need its parameter to be final, when propagating it to another functional call.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • Ooh - hadn't spotted that the requirement is removed in Java 8 (although as you note it's only the "being explicit" part which is removed.) – Jon Skeet Sep 19 '13 at 07:35
  • This is because Java 8 supports closures, so you don't need to implicitly declared final is no longer required (because it's a closure instead). The result is the same tho'. – Mikkel Løkke Sep 19 '13 at 09:53
7

It seems to me that you're getting confused between a variable being declared final, and it being a constant.

The compiler doesn't replace all references to local variables with a constant - but when the instance of the anonymous class is constructed, the current value of each relevant variable is passed to the constructor, and stored in a variable within the anonymous class. That's just as fine for a parameter as for any other kind of local variable.

So this code:

public static void method(final int x) {
    Runnable r = new Runnable() {
        @Override public void run() {
            System.out.println(x);
        }
    };
    r.run();
}

is broadly equivalent to:

public static void method(final int x) {
    Runnable r = new AnonymousRunnable(x);
    r.run();
}

private static class AnonymousRunnable implements Runnable {
    private final int x;

    AnonymousRunnable(int x) {
        this.x = x;
    }

    @Override public void run() {
        System.out.println(x);
    }
}

I've made both the method and the nested class static to avoid worrying about whether this is captured or not.

The local variable has to be final when it's captured to avoid situations which could otherwise be confusing. Suppose that weren't the case - consider this example:

void method() {
    int x = 10;
    Runnable r = new Runnable() {
        @Override public void run() {
            System.out.println(x);
        }
    };
    x = 20;
    r.run(); // Should this print 10 or 20?
}

Using the current way that anonymous classes work, but just removing the final restriction, it would print 10... but developers might expect it to print 20. Likewise, you should consider what would happen if you modified x within the run method. (As noted in Joop's answer, in Java 8 captured local variables are "effectively final" - so they act as if you've declared them to be final, but without doing so explicitly.)

As an example of a different approach, C# handles closures (for anonymous functions) in a different way, hoisting the local variables to a sort of anonymous class so that they can be modified. It's a more complex approach, but a bit more flexible. You might find my article about closures in Java and C# useful.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks for clarification. That's the way what I think about it. So I do not understand why it must be defined as `final` when the anonymous creation just use snapshot of all properties used in and thats all. This approach works even w/o `final` keyword as I wrote in my question. – Martin Podval Sep 19 '13 at 07:26
  • Maybe I didn't get it - but your second example could be written without declaring both variables as `final` and the first does require `x` to be declared final because of the use inside the anonymous class - and that's what the OP asked us to clarify. – Nir Alfasi Sep 19 '13 at 07:26
  • @alfasin: Again, see if the edit makes things clearer. The first part explains what actually happens as the OP seemed to think that the actual *value* (if it's a constant) would be baked into the anonymous class... whereas it's really a *variable* whose value is initialized based on the value of the local variable at the point of instantiation. – Jon Skeet Sep 19 '13 at 07:30
  • Ok, so the point is possible confusion regarding `stale` value. – Martin Podval Sep 19 '13 at 07:30
  • +1 Thanks for the info! I still don't understand the logic behind the decision - the architecture in C# sounds more reasonable to me :) – Nir Alfasi Sep 19 '13 at 07:34
  • @MartinPodval: I don't think I'd use the word "stale" (it sounds like a concern around memory barriers etc) but I think you've got broadly the right idea. – Jon Skeet Sep 19 '13 at 07:35
  • @JonSkeet that's the reason why I used "possibly stale" but I'm sure that we understand each other :-) – Martin Podval Sep 19 '13 at 07:39
-2

I think you get confused because you used base types. If you think about references it should get more clear.

You are right at the creation an anonymous class copies all the references to its own context. And to be allowed to do that all the used local variables (and parameters are just another kind of local variables) have to be final. So it is not about the value, it is about the reference. And base types are a special case in java (that is sad). They treated like references in that case.

Hope this clarifies this final problem.

Sorontur
  • 500
  • 4
  • 15
  • 1
    No, there's no special case here. In every case, the value of the variable is copied to the anonymous class - whether that value is a reference or a primitive value. I don't think there's *any* benefit in differentiating between classes and primitive types here. – Jon Skeet Sep 19 '13 at 07:19
  • i never said something like that, please read things correctly i refered on the example the question provided – Sorontur Sep 19 '13 at 07:21
  • I've read your answer multiple times, and I still can't see how it's helpful at all... in particular "They treated like references in that case" is just wrong. The creation of an anonymous class instance just copies the *values* of the local variables into the the new instance. Whether those values are references or primitive values ("base" isn't part of standard Java terminology) is irrelevant, and your focus on it is distracting, IMO. – Jon Skeet Sep 19 '13 at 07:32