我正在学习核心Java'本书讲述了本地内部类如何从外部方法访问变量。虽然,我对某些事感到困惑。
这本书提供了一段代码:
public void start(int interval, boolean beep)
{
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is " + new Date());
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
这本书说明了控制流程如何:
start
方法。listener
已初始化。listener
引用传递给Timer
构造函数。 此时,beep
方法的start
参数变量不再存在。 actionPerformed
方法会执行if (beep) ...
我只是为什么哔哔声不再存在而感到困惑?我认为外部启动方法仍然没有结束},局部变量仍然可以存在......
答案 0 :(得分:2)
编译器知道beep
有效地最终,因此可以假设它不会改变。然后它将安排构建本地对象,如:
class TimePrinter implements ActionListener {
// Capture the local variable in this instance.
boolean instanceBeep = beep;
public void actionPerformed(ActionEvent event) {
System.out.println("At the tone, the time is " + new Date());
if (instanceBeep) Toolkit.getDefaultToolkit().beep();
}
}
但是,这仅在beep
参数有效最终时才有效,即它不会更改,甚至可以在不破坏代码的情况下声明final beep
。
要确认,只需尝试以下内容:
public void start(int interval, boolean beep) {
class TimePrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
System.out.println("At the tone, the time is " + new Date());
// Error:(17, 21) java: local variables referenced from an inner class must be final or effectively final
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
// Add this to see the compiler working it out.
beep = false;
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
并且看到现在不允许访问beep
参数,因为它不再是有效的最终。
请参阅this site或effectively final上的官方Oracle文档。
答案 1 :(得分:1)
如果这是本书的确切引用,那么这本书就错了。你是正确的beep
函数的start
参数在结束括号}
之前不会超出范围。使用本地类没有引起特殊行为。
在这种情况下,一个变量据说是"captured"。
变量需要被捕获final or "effectively final" - 即永远不能重新分配。
在您的情况下,您不会修改beep
的值,因此它实际上是最终的并且有资格被捕获。
创建TimePrinter
对象时,beep
的值将复制到其内部状态。这是因为变量beep
在需要使用时可能已超出范围。
启动计时器,start
方法结束。原始beep
现已超出范围。现在将调用每隔几秒钟(取决于interval
)actionPerformed
。原始的beep
已经超出范围,但我们复制了它的值,因此我们的函数仍然能够按照我们的预期执行。
这几乎是作者试图表达的想法。