简体   繁体   English

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

[英]Can't compile runnable that calls itself?

I'm trying to make a Runnable that calls itself: 我正在尝试制作一个可调用自身的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
      }
    };
  }
}

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)? Runnable的标准库定义中是否有什么特别之处,它消除了保证在使用接口实现之前对其进行了完全定义的保证(例如,当您实现该接口实现时,它就会运行一些代码)?

Can't compile runnable that calls itself? 无法编译调用自身的runnable吗?

You can -- just don't qualify the run() method's name with r ; 您可以-只是不用用r限定run()方法的名称; 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. 代码中的问题是,您试图在变量r完全初始化之前使用它。 It's kind of like: 有点像:

int x = x;

which of course is invalid (and produces the same error message). 当然哪个无效(并产生相同的错误消息)。

This has nothing to do with Runnable. 这与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. 在这种情况下,编译器没有一种简单的方法来知道是否/在实际执行变量表达式时确实分配了 r ,从而完全禁止访问。

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. 在访问任何值时 ,每个局部变量(第14.4节)和最后一个空白字段(第4.12.4节,第8.3.1.2节)都必须具有一个明确分配的值。

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. 但是我无法想到为什么它不仅仅假设在定义接口实现时就定义了r。

In the above example a case is shown where the variable r is definitely accessed before it is assigned. 在上面的示例中,显示了在分配变量r之前肯定访问变量r情况。 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. 同样,如果构造函数调用了虚拟方法(例如run ),则可以在分配它之前对其进行访问-但这种情况在整个编译单元中都无法检测到。

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. 这是进一步问题的final和匿名访问类型:Java 没有在最后的变量创建一个闭包,但是当它创建匿名类型的实例,而结合这些变量的值 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. 这将在对变量的访问(实际上是对先前绑定的值的访问)与匿名实例的创建之间创建无效的循环关系。

You could save this pointer and call Run method in a new Runnable : 您可以保存此指针,然后在新的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(); } });
        }
    };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM