2

在以下示例中,它通过将参数设置为来工作和int i编译final

class Miner1
{
    Miner getMiner(final int i) {
        return new Miner() {                
           public void perform_work() { 
              System.out.println(i);
           }
        };
    }

interface Miner { void perform_work(); }

否则,如果没有像前面的示例那样设置为 final,它将无法编译。有人知道为什么吗?即使没有,它也应该在范围内,final因为花括号尚未关闭。

提前致谢。

4

4 回答 4

4

这与范围无关,而是与匿名内部类有关。

您不能从在父类中声明的匿名类访问局部变量,除非该变量是final.

看看关于 SO另一个问题,它解释了逻辑。

于 2013-03-07T11:07:40.350 回答
3

它与范围没有任何关系。

在 Java 中,匿名类只能从final.

JLS

必须声明任何使用但未在内部类中声明的局部变量、形式参数或异常参数final

于 2013-03-07T11:06:34.933 回答
2

Java 只允许从定义在方法/构造函数中的匿名类引用最终变量/参数。这是为了使代码的行为更直观。变量/参数的值在创建实例时通过隐藏的构造函数参数传递给匿名类的实例,因此匿名类的实例无法跟踪变量的进一步变化。如果允许访问非最终变量,则可以编写以下内容:

int a = 5;

Thread t = new Thread ()
{
    @Override
    public void run ()
    {
        System.out.println (a); // This will print 5, rather than 6!
    }
};

a = 6;

t.start ();

并期望6被打印出来。要理解为什么上面的代码会打印5,请注意这段代码等价于以下代码:

class MyThread extends Thread
{
    int _a;

    public MyThread (int a)
    {
        this._a = a;
    }

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

int a = 5;

Thread t = new MyThread (a); // Value `5` is passed

a = 6;

t.start (); // Value `5` passed to the constructor earlier is printed here
于 2013-03-07T11:08:59.720 回答
0

这是因为,在 Java 中,匿名内部类只能访问 final 局部变量和类的字段。
现在的问题是为什么?
这是因为方法的局部变量(getMiner在您的情况下)存在于堆栈中,并且仅在方法的生命周期内存在。局部变量的范围仅限于声明变量的封闭方法。当方法结束时,堆栈帧被吹走,变量是历史。但是即使在方法完成之后,在其中创建的内部类对象可能仍然在堆上存活。如果例如Miner通过获得匿名内部类的引用getMiner被传递到代码的其他位置并在那里使用,因为局部变量已经被炸毁,这对于该对象来说将是一个奇怪的情况。此问题的一种解决方案可能是匿名内部类对象制作局部变量的副本。但同样不能保证匿名内部类对象会看到该变量的最新值,因为局部变量容易受到更改。

但是如果局部变量是final的,它保证了变量值在初始化后在任何情况下都不会改变,所以方法局部内部类可以只复制它以供私有使用,即使在原始值之后仍然存在已从堆栈中移除。

于 2013-03-07T11:24:51.460 回答