Why does the compiler accept non-final variables in internal classes?
I’m creating a MediaPlayer with seekbar. Here, mp is a MediaPlayer object and seeker is a seekbar object created in the MainActivity class.
My understanding is that anonymous inner classes can only access final members, so how does runnable access these objects (MP and seeker).
h = new Handler(); create a handler for the MainActivity
create a runnable activity to change the progress of seekbar in MainThread for every 1 sec
Runnable r = new Runnable() {
@Override
public void run() {
if (mp != null) {
seeker.setProgress(mp.getCurrentPosition() / 1000);
}
h.postDelayed(this, 1000);
}
};
this.runOnUiThread(r);
Note: The code runs perfectly. Thanks for your help.
Solution
It happens because there’s something called effective finality.
Any local variable, formal parameter, or exception parameter used but not declared in an inner class must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.
If a variable is effectively final, adding the final modifier to its declaration will not introduce any compile-time errors. Conversely, a local variable or parameter that is declared final in a valid program becomes effectively final if the final modifier is removed.
When a variable is set only once and used in an inner class, the compiler changes the variable’s modifier to final
.
Consider this code for Tests.java:
public class Tests {
public static void main(String[] args) throws Throwable {
String value = "Hello";
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(value);
}
};
}
}
The above code produces the following code when compiled. See the compiler change the modifier for value
to last?
Tests.class (decoded):
public class Tests {
public Tests() {
}
public static void main(String[] args) throws Throwable {
final String value = "Hello";
Runnable var10000 = new Runnable() {
public void run() {
System.out.println(value);
}
};
}
}
However, if you change the value of a variable (see code below), it won’t be compiled because the compiler knows it’s not a valid final variable:
public class Tests {
public static void main(String[] args) throws Throwable {
String value = "Hello";
value = "world";
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(value);
}
};
}
}