I would like some clarification beyond what I have found online involving final variables and inner classes. Specifically, my question pertains to why global variables are ok to use in inner classes rather than why certain variables need to be final. Please let me know if you can provide clarification/corrections on my reasoning.
In the following examples, I created an inner class, and from what I believe “i” is duplicated when the anonymous inner class which is of the type View.OnClickListener is created. When the inner class is created the variables must be made final more as a note to the user than something that is really necessary. From what I gather, this is just like when you pass a variable into a method. Those variables are duplicated since Java is a pass by value language. After the method finishes the copies are lost and no changes to the originals values are saved. Let’s just stick to the int to keep it simple for this example.
Button b;
int j = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
b=findViewById(R.id.button);
int i = 0;
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
i ;
j
}
});
}
Is the forcing of final is more to safe guard the programmer since the value of i (in this example) will never actually be changed?
Is the final modifier not necessary for j because global variables are not duplicated when the inner class is created? Is this because they already exist for all classes that are contained within, so duplication do not take place?
CodePudding user response:
It basically has to do with how java is built. Think how this must work under the hood. In an inner class like this, it has access to the "this" reference of the containing instance. So "this" tells the inner class where the data for the containing instance is in memory and then the offset where to find the int. That is a constant (well not entirely but that is transparent to you and is made to look like that way basically). So the class knows where to get the data and update it. The local variables use a different memory area (stack) and it is finite (think your good old friend stack overflow.) Generally when the method is done and it doesn't need that state anymore, that data is cleaned up. But the class can live longer than the method's lifecycle, so if you created an inner class in a method like this, it would have to tie all of the method state to the inner class, so it can be used if needed. This is called a closure, something Java doesn't generally support (I don't remember about the newest or planned versions since I know it was at least talked about.) Final tells the compiler that the value of the variable won't change. So if you have an int and it is set to 4 then it is always 4. This means the inner class doesn't need to know where to find the int, it can just copy it since it will always be that value. Now if it is a reference then the final means the reference won't change or where that data lives in memory won't change, so it can continue to know where to go to look for that. Now to tie with above all non-static methods basically have a final reference to "this" which is implicitly there, so it can be passed by default to the inner class.
Thanks for reading my ted talk that I am sure someone will say "well aktually..." to, but this is the high-level gist of it.
CodePudding user response:
I will use this example in my answer :
int j = 0;
protected void method() {
int i = 0;
run(() -> {
i ; // error
j ; // no error
});
}
The level of accessibility does everything. Such as j
is a global variable that will stay until the object exist, it will always be here.
The lambda expression (() -> {}
) will use the object and keep it.
For i
, the variable will be removed at the end of the method execution. Such as the lambda is not runned by the method itself, the JVM don't know the area of the variable.
Without final, it consider that the variable isn't used my lambda and can delete it to get back memory space.
With it, it consider it should wait until all sub-method are finished. So until all lambda's in lambda's have ended.