不能引用在其他方法中定义的内部类中的非最终变量

2022-08-31 05:38:04

已编辑:我需要更改多个变量的值,因为它们通过计时器运行多次。我需要通过计时器在每次迭代时不断更新值。我无法将值设置为 final,因为这将阻止我更新值,但是我遇到了在下面的初始问题中描述的错误:

我以前写过下面的内容:

我收到错误“无法引用在不同方法中定义的内部类中的非最终变量”。

这发生在称为 price 的双倍和称为 priceObject 的价格中。你知道我为什么会遇到这个问题吗?我不明白为什么我需要有一个最终声明。另外,如果你能看到我正在尝试做什么,我必须做些什么来解决这个问题。

public static void main(String args[]) {

    int period = 2000;
    int delay = 2000;

    double lastPrice = 0;
    Price priceObject = new Price();
    double price = 0;

    Timer timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            price = priceObject.getNextPrice(lastPrice);
            System.out.println();
            lastPrice = price;
        }
    }, delay, period);
}

答案 1

Java不支持真正的闭包,即使使用像你在这里使用的匿名类()看起来像一种闭包。new TimerTask() { ... }

编辑 - 请参阅下面的评论 - 正如KeeperOfTheSoul所指出的那样,以下不是正确的解释。

这就是为什么它不起作用的原因:

变量和价格是 main() 方法中的局部变量。使用匿名类创建的对象可能会一直持续到该方法返回之后。lastPricemain()

当方法返回时,局部变量(如 and )将从堆栈中清理出来,因此它们在返回后将不再存在。main()lastPricepricemain()

但匿名类对象引用这些变量。如果匿名类对象在清理变量后尝试访问变量,事情将变得非常错误。

通过使 和 ,它们不再是真正的变量,而是常量。然后,编译器只需将匿名类中的使用替换为常量的值(当然是在编译时),您将不再遇到访问不存在的变量的问题。lastPricepricefinallastPriceprice

其他支持闭包的编程语言通过专门处理这些变量来做到这一点 - 通过确保它们在方法结束时不会被破坏,以便闭包仍然可以访问变量。

@Ankur:你可以这样做:

public static void main(String args[]) {
    int period = 2000;
    int delay = 2000;

    Timer timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {
        // Variables as member variables instead of local variables in main()
        private double lastPrice = 0;
        private Price priceObject = new Price();
        private double price = 0;

        public void run() {
            price = priceObject.getNextPrice(lastPrice);
            System.out.println();
            lastPrice = price;
        }
    }, delay, period);      
}

答案 2

为了避免匿名委托引用的java变量中的闭包的奇怪副作用,必须将其标记为最终,因此要在计时器任务中引用和定价,需要将它们标记为最终。lastPrice

这显然不适合你,因为你希望改变它们,在这种情况下,你应该考虑将它们封装在一个类中。

public class Foo {
    private PriceObject priceObject;
    private double lastPrice;
    private double price;

    public Foo(PriceObject priceObject) {
        this.priceObject = priceObject;
    }

    public void tick() {
        price = priceObject.getNextPrice(lastPrice);
        lastPrice = price;
    }
}

现在只需创建一个新的Foo作为最终版本,然后从计时器调用.tick。

public static void main(String args[]){
    int period = 2000;
    int delay = 2000;

    Price priceObject = new Price();
    final Foo foo = new Foo(priceObject);

    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            foo.tick();
        }
    }, delay, period);
}