无法编译自己调用的runnable?

时间:2014-04-26 22:40:52

标签: java variable-assignment runnable

我正在努力制作一个自称为Runnable的人:

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

收到此错误:

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

为什么呢?在Runnable的标准库定义中是否存在一些特殊的东西,它消除了在使用之前完全定义接口实现的保证(例如,它有一些代码可以在您执行它时运行) ?

3 个答案:

答案 0 :(得分:3)

  

无法编译调用自身的runnable吗?

您可以 - 只是不要使用run()限定r方法的名称;你可以毫不含糊地引用一种方法:

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

代码中的问题是您在完全初始化之前尝试使用变量r。有点像:

int x = x;

当然无效(并产生相同的错误信息)。

答案 1 :(得分:2)

这与Runnable无关。相反,这个编译器错误的结果是因为赋值右侧的表达式在赋值之前发生了

在这种情况下,如果/当变量表达式实际执行时,编译器没有一种简单的方法来知道r是否为definitely assigned因此禁止完全访问。

  

每个局部变量(第14.4节)和每个空白最终字段(§4.12.4,§8.3.1.2)在对其值进行任何访问时都必须具有明确赋值。

请考虑以下内容,说明问题并使用与最初发布的示例相同的编译器错误失败。

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

回复Dog的评论:

  

..当我今天遇到错误时,这完全是我的想法。但我想不出任何理由为什么它不仅仅假设r是由定义接口实现的时间定义的。

在上面的示例中,显示了一个案例,其中变量r在分配之前已经明确访问。同样,如果构造函数调用虚拟方法(比如说run),那么可以在分配之前访问 - 但是在编译单元中无法检测到这种情况。

这对于final和匿名类型的访问更加成问题:Java 在最终变量上创建闭包,而是绑定值这些变量在创建匿名类型的实例时。这会在访问变量(实际上是对先前绑定的值的访问权限)和创建匿名实例之间创建无效的循环关系。

答案 2 :(得分:0)

您可以保存此指针并在新的Runnable中调用Run方法:

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