Java – Why does the compiler accept non-final variables in internal classes?

Why does the compiler accept non-final variables in internal classes?… here is a solution to the problem.

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.

jls-8.1.3:

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.

jls-4.12.4:

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);
           }
       };
   }
}

Related Problems and Solutions