“变量示例可能尚未初始化”在匿名课堂上 [英] "Variable example might not have been initialized" in anonymous class

查看:151
本文介绍了“变量示例可能尚未初始化”在匿名课堂上的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

自行回答的问题受到变量'snackbar'可能尚未初始化。我觉得有更多细节可以更好地与特定问题分开添加。

This self-answered question was inspired by Variable 'snackbar' might not have been initialized. I felt that there was more detail which would be better added separate from that specific question.

为什么下面的代码不能被编译?

Why can the following code not be compiled?

public class Example {
  public static void main(String[] args) {
    final Runnable example = new Runnable() {
      @Override
      public void run() {
        System.out.println(example);  // Error on this line
      }
    };
  }
}

编译错误:

error: variable example might not have been initialized


推荐答案

这是因为匿名类的实现方式。你可以看到这个,如果你对代码稍作修改然后反编译:

This occurs because of the way that anonymous classes are implemented. You can see this if you make a slight change to the code and then decompile:

    final Runnable other = null;
    final Runnable example = new Runnable() {
      @Override
      public void run() {
        System.out.println(other);
      }
    };

即。使匿名类引用不同的局部变量。现在这将编译;我们可以使用 javap 进行反编译,并查看匿名类的界面:

i.e. make the anonymous class refer to a different local variable. This now will compile; we can decompile using javap and see the interface of the anonymous class:

final class Example$1 implements java.lang.Runnable {
  final java.lang.Runnable val$other;
  Example$1(java.lang.Runnable);
  public void run();
}

示例$ 1 是Java内部引用匿名类的名称。)

(Example$1 is the name by which Java internally refers to the anonymous class).

这表明编译器已经为匿名类添加了一个构造函数,该构造函数需要 Runnable 参数;它还有一个名为 val $ other 的字段。此字段的此名称应提示此字段与其他本地变量相关。

This shows that the compiler has added a constructor to the anonymous class which takes a Runnable parameter; it also has a field called val$other. This name of this field should hint that this field is related to the other local variable.

您可以深入研究字节码进一步,并看到此参数被分配给 val $ other

You can dig into the bytecode further, and see that this parameter is assigned to val$other:

  Example$1(java.lang.Runnable);
    Code:
       0: aload_0
       // This gets the parameter...
       1: aload_1  
       // ...and this assigns it to the field val$other
       2: putfield      #1                  // Field val$other:Ljava/lang/Runnable;
       5: aload_0
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return

因此,这表明匿名类从其封闭范围访问变量的方式:它们只是在构造时传递值。

So, what this shows is the way that anonymous classes access the variables from their enclosing scope: they are simply passed the value at construction time.

这应该有希望说明为什么编译器会阻止你编写问题中的代码:它需要能够将引用传递给 Runnable 到匿名类以构造它。但是,Java评估以下代码的方式:

This should hopefully show why the compiler stops you from writing code such as that in the question: it needs to be able to pass the reference to the Runnable to the anonymous class in order to construct it. However, the way that Java evaluates the following code:

final Runnable example = new Runnable() { ... }

首先完全评估右侧,然后将其分配给左侧的变量侧。但是,它需要右侧的变量值才能传递给生成的构造函数 Runnable $ 1

is to fully evaluate the right-hand side first, and then assign it to the variable on the left-hand side. However, it needs the value of the variable on the right-hand side in order to pass into the generated constructor of Runnable$1:

final Runnable example = new Example$1(example);

先前未声明示例这不是问题,因为此代码在语义上与以下内容相同:

That example hasn't been previously declared is not a problem, since this code is semantically identical to:

final Runnable example;
example = new Example$1(example);

所以你得到的错误并不是无法解析变量 - 但是, example 在用作构造函数的参数之前尚未赋值,因此编译错误。

so the error that you get isn't that the variable cannot be resolved - however, example hasn't been assigned a value before it is used as an argument to the constructor, hence the compiler error.

可能有人认为这只是一个实现细节:参数必须传递给构造函数并不重要,因为 run()方法可以在赋值之前调用。

It might be argued that this is simply an implementation detail: it shouldn't matter that the argument has to be passed into the constructor, as there is no way that the run() method can be invoked prior to the assignment.

实际上,这不是真的:你可以调用在赋值之前运行(),如下所示:

Actually, that's not true: you can invoke run() before the assignment, as follows:

final Runnable example = new Runnable() {
  Runnable runAndReturn() {
    run();
    return this;
  }

  @Override public void run() {
    System.out.println(example);
  }
}.runAndReturn();

如果在匿名类中引用 example 被允许,你可以写这个。因此,不允许参考该变量。

If referring to example inside the anonymous class were allowed, you would be able to write this. Hence, referring to that variable is disallowed.

这篇关于“变量示例可能尚未初始化”在匿名课堂上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆