我正在努力制作一个自称为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
的标准库定义中是否存在一些特殊的东西,它消除了在使用之前完全定义接口实现的保证(例如,它有一些代码可以在您执行它时运行) ?
答案 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(); } });
}
};