Why do variables passed to runnable need to be final?
Asked Answered
R

3

11

If I have a variable int x = 1, say, and I declare a runnable in the main thread, and I want to pass x to the runnable's run() method, it must be declared final. Why?

final int x = 0;//<----must be final...
private class myRun implements Runnable {

    @Override
    public void run() {
        x++;//
    }

}
Raggletaggle answered 11/7, 2012 at 2:33 Comment(2)
Because that is how the language is defined. Presumably to keep the variables from being modified within said method in the anonymous inner class. (I do believe it also simplifies the implementation: only the values need to be proxy-copied in to the anonymous type and the original variables need to no longer be kept, as would be required with full closure semantics.)Ethyne
If this wasn't the case, your variables could get modified any time without warning.Myrnamyrobalan
P
10

Because if they are able to be changed, it could cause a lot of problems, consider this:

public void count()
{
    int x;

    new Thread(new Runnable()
    {
        public void run()
        {
            while(x < 100)
            {
                x++;
                try
                {
                    Thread.sleep(1000);
                }catch(Exception e){}
            }
        }
     }).start();

     // do some more code...

     for(x = 0;x < 5;x++)
         for(int y = 0;y < 10;y++)
             System.out.println(myArrayElement[x][y]);
 }

This is a rough example but you can see where a lot of unexplained errors could occur. This is why the variables must be final. Here is a simple fix for the problem above:

public void count()
{
    int x;

    final int w = x;

    new Thread(new Runnable()
    {
        public void run()
        {
            int z = w;

            while(z < 100)
            {
                z++;
                try
                {
                    Thread.sleep(1000);
                }catch(Exception e){}
            }
        }
     }).start();

     // do some more code...

     for(x = 0;x < 5;x++)
         for(int y = 0;y < 10;y++)
             System.out.println(myArrayElement[x][y]);
 } 

If you want a more full explanation, it is sort of like synchronized. Java wants to prevent you from referencing one Object from multiple Threads. Here is a little bit about synchronization:

Hope this helped!

Polygamy answered 11/7, 2012 at 2:45 Comment(3)
This would't cause a lot of problems -- many languages have full closures :-) The fact that Runnable (and presumably threads) is in the topic here is orthogonal to allowing local variables to be modified from anonymous inner types. Even if talking about something not dealing with threading, it would be the same language rules. Also, the same threading issue would result from member variables ..Ethyne
In your "Here is a simple fix for the problem above:", won't w++ give you compilation error?! You cannot reassign w after initializing it.Katelynnkaterina
@Katelynnkaterina I cannot believe nobody has said anything until now, thanks for finding that.Polygamy
L
11

Because that's what the language specification says. According to Guy Steele, the rationale behind this choice is that programmers would expect the declaration int x = 0 in a method to result in stack-allocated storage, but if you can return a new myRun() from the method (or otherwise let a myRun persist past the function's return) and you can modify it afterwards, then x has to be heap-allocated instead to have the semantics you'd expect.

They could have done that, and in fact other languages have done it that way. But the Java designers decided instead to require that you mark x as final to avoid requiring implementations to heap-allocate what looks like stack-allocated storage.

(I should note: this isn't specific to Runnable. It applies to any anonymous inner class.)

Lighting answered 11/7, 2012 at 2:42 Comment(0)
P
10

Because if they are able to be changed, it could cause a lot of problems, consider this:

public void count()
{
    int x;

    new Thread(new Runnable()
    {
        public void run()
        {
            while(x < 100)
            {
                x++;
                try
                {
                    Thread.sleep(1000);
                }catch(Exception e){}
            }
        }
     }).start();

     // do some more code...

     for(x = 0;x < 5;x++)
         for(int y = 0;y < 10;y++)
             System.out.println(myArrayElement[x][y]);
 }

This is a rough example but you can see where a lot of unexplained errors could occur. This is why the variables must be final. Here is a simple fix for the problem above:

public void count()
{
    int x;

    final int w = x;

    new Thread(new Runnable()
    {
        public void run()
        {
            int z = w;

            while(z < 100)
            {
                z++;
                try
                {
                    Thread.sleep(1000);
                }catch(Exception e){}
            }
        }
     }).start();

     // do some more code...

     for(x = 0;x < 5;x++)
         for(int y = 0;y < 10;y++)
             System.out.println(myArrayElement[x][y]);
 } 

If you want a more full explanation, it is sort of like synchronized. Java wants to prevent you from referencing one Object from multiple Threads. Here is a little bit about synchronization:

Hope this helped!

Polygamy answered 11/7, 2012 at 2:45 Comment(3)
This would't cause a lot of problems -- many languages have full closures :-) The fact that Runnable (and presumably threads) is in the topic here is orthogonal to allowing local variables to be modified from anonymous inner types. Even if talking about something not dealing with threading, it would be the same language rules. Also, the same threading issue would result from member variables ..Ethyne
In your "Here is a simple fix for the problem above:", won't w++ give you compilation error?! You cannot reassign w after initializing it.Katelynnkaterina
@Katelynnkaterina I cannot believe nobody has said anything until now, thanks for finding that.Polygamy
V
2

The big 'issue' with multithreading, and also the entire reason for using it, is that multiple things are happening at the same time. All of a sudden, the value of any variable that your thread accesses that isn't local to the thread can change at any point. Thus, you may thing you're just printing the numbers 1-10 with this code:

int x = 0;  //supposing that this was allowed to be non-final...
   private class myRun implements Runnable{

    @Override
    public void run() {
        for (int i=0; i<10; i++ ) {
            System.Out.Println( x++ );
        }
    }
}

But in reality, if other code in that class changes the value of x, you could end up printing 230498 - 230508. The value of x could event change in the middle of your loop. If you can't rely on x having a certain value or keeping a value you assigned to it previously, it becomes futile to use it in your code. Why would you use a variable if its contents could change at the drop of a hat?

Rather than just forbidding you to use it at all, Java requires that you make it final. You could just 'promise' to never change the value of x from another thread, but then why not make it final in the first place and let the compiler help you out? Granted, you can only access the initial value assigned to x, but just being able to access the variable's initial value is better than not being able to use it at all, which would effectively cut off the thread's ability to utilize the data from the rest of your class.

Villareal answered 11/7, 2012 at 2:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.