End field and anonymous class

I am still not satisfied with the explanation of the anonymous class and the final field. There were many questions that tried to explain the obvious problem, but I did not find the answers to all my questions :-)

Assume the 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); } }; } 
  • This code cannot be compiled due to the "unfinal" k property.
  • I understand that the compiler can replace the z property with a declared value at compile time.

When I was looking for a solution on how i and x can work, I found this answer that says:

Then the compiler can simply replace using lastPrice and price in an anonymous class with constant values โ€‹โ€‹(at compile time, of course), and you will no longer have problems accessing non-existent variables

How can it work for fields i and x if they are method parameters? They are not known at compile time? This approach may 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 the copy as a field in the inner class. As soon as the external method completed and its stack stack was deleted, the original variable disappeared, but the internal private copy of the class is stored in the memory of its own class

I would understand that an anonymous class somehow copied all the necessary content (fields) during its creation. The absence of final has an obvious problem: if some code below the anonymous class declaration changes the value, then stale values โ€‹โ€‹are possible as a result of execution.

But this is normal, this should solve the problem when the method of the anonymous class is executed from the scope of the used properties.

But this approach should work even without a final declaration, since it just copies all the fields.

Both approaches seem independent to me. Speaking of which - and this may solve my questions - I have not found how the final method method works. They are not removed from the stack, even if the method exits? This seems nonsense to me, but it explains a lot of things :-)

What is the correct answer?

+6
source share
3 answers

It seems to me that you are confused between the declared variable final and the constant.

The compiler does not replace all references to local variables with a constant, but when an instance of an anonymous class is constructed, the current value of each corresponding variable is passed to the constructor and stored in the variable in the anonymous class. This is as good for a parameter as it is for any other 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(); } 

generally 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 made both a method and a nested static class so as not to worry about whether this captured or not.

The local variable must be final when it was captured in order to avoid situations that might otherwise be confusing. Assuming this is not 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 anonymous classes work, but just removing the final constraint, it will print 10 ... but developers can expect it to print 20. Similarly, you should think about what happens if you change x as part of the run method. (As noted in Joop's answer, in Java 8, captured local variables are "actually final" - so they act as if you declared them final, but without doing so explicitly.)

As an example of another approach, C # handles closures (for anonymous functions) differently, raising local variables to a kind of anonymous class so that they can be modified. This is a more complex approach, but a bit more flexible. You can find my article on closure in Java and C # .

+7
source

Due to the need to copy variables from a method to an anonymous class (as described) there was a directive decision to require that the variable copied be final. Thus, assignment in any method or anonymous class will not give obsolete values, and the code will be more consistent.

But! In Java 8, this requirement is softened: final no longer required if the variable is de facto final: assignments are not allowed after the variable is "copied" in an anonymous class.

This makes sense because of the many functional notations. Otherwise, the actionPerformed button actionPerformed suddenly be needed so that its parameter is final when extending it to another function call.

+8
source

I think you are confused because you used basic types. If you are thinking of links, this should become more clear.

You are right when creating an anonymous class by copying all references into your own context. And for this it is necessary that all the local variables used (and the parameters are just one kind of local variables) should be final. So, this is not about meaning, but about reference. And basic types are a special case in java (this is sad). In this case, they were considered as links.

Hope this clarifies this last issue.

-2
source

Source: https://habr.com/ru/post/954140/


All Articles