Question

I'm trying to make a Runnable that calls itself:

class A {
  public void f() {
    final Runnable r = new Runnable() {
      public void run() {
        // do stuff, return if it worked.
        r.run(); // otherwise try again
      }
    };
  }
}

It gets this error:

$ javac A.java
A.java:6: error: variable r might not have been initialized
        r.run(); // otherwise try again
        ^
1 error

Why? Is there something special in the standard library definition of Runnable which removes the guarantee that the interface implementation is fully defined before it is used (perhaps for example, it has some code that gets run whenever you make an implementation of it)?

Was it helpful?

Solution 2

This has nothing to do with Runnable. Rather, this compiler error results because the expression on the right-hand side of the assignment happens before the assignment.

In this case, the compiler doesn't have a trivial way of knowing if r is definitely assigned if/when the variable expression is actually executed and thus forbids the access entirely.

Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.

Consider the following which illustrates the issue and fails with the same compiler error as the originally posted example.

final String r = (new Object() {
    public String toString() {
        // -> error: variable r might not have been initialized
        // (And in this case it is indeed *not* assigned!)
        return "Hello " + r + "!";
    }
}).toString();

Reply to Dog's comment:

.. This was my thought exactly, when I came across the error today. But I can't think of any reason why it doesn't just assume r is defined by the time the interface implementation is defined.

In the above example a case is shown where the variable r is definitely accessed before it is assigned. Likewise, if the constructor called a virtual method (say run) then it could be accessed before it is assigned - but this case can't be detected across a compilation unit.

This is further problematic with final and access in the anonymous type: Java does not create a closure over the final variables, but rather binds the values of those variables when it creates an instance of the anonymous type. This creates an invalid cyclic relation between access to the variable (which is really access to the previously bound value) and the creation of the anonymous instance.

OTHER TIPS

Can't compile runnable that calls itself?

You can -- just don't qualify the run() method's name with r; you can unambiguously refer to a method within itself:

class A {
  public void f() {
    final Runnable r = new Runnable() {
      public void run() {
        ...
        run();  // notice that there is no `r.`
      }
    };
  }
}

The problem in your code is that you're trying to use the variable r before it's fully initialized. It's kind of like:

int x = x;

which of course is invalid (and produces the same error message).

You could save this pointer and call Run method in a new Runnable :

    Runnable r = new Runnable() {
        @Override public void run() {
            // if condition met and need to run more
            final Runnable that = this;
            executor.execute(new Runnable() { @Override public void run() { that.run(); } });
        }
    };
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top