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

我之前写过如下内容:

我得到错误“不能引用在不同方法中定义的内部类中的非最终变量”。 这发生在名为price的double和名为priceObject的price上。你知道我为什么会有这个问题吗?我不明白为什么我要做最后申报。如果你能看到我在做什么,我要怎么做才能解决这个问题。

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);
}

当前回答

为了解决上述问题,不同的语言做出了不同的决定。

对于Java,解决方案就像我们在本文中看到的那样。

对于c#,解决方案是允许副作用和引用捕获是唯一的选择。

对于c++ 11,解决方案是让程序员来做决定。他们可以选择通过值或引用来捕获。如果通过值捕获,则不会发生副作用,因为引用的变量实际上是不同的。如果通过引用捕获,可能会产生副作用,但程序员应该意识到这一点。

其他回答

将该变量声明为静态变量,并使用className.variable在所需的方法中引用它

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

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

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作为final,并从计时器调用.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);
}

使用匿名类时,只能从包含类中访问最终变量。因此,您需要声明正在使用的变量为final(这对您来说不是一个选项,因为您正在更改lastPrice和price),或者不要使用匿名类。

因此,您的选择是创建一个实际的内部类,您可以在其中传递变量并以正常方式使用它们

or:

对于你的lastPrice和price变量,有一个快速(在我看来很难看)的方法,就是像这样声明它

final double lastPrice[1];
final double price[1];

在匿名类中,你可以这样设置值

price[0] = priceObject.getNextPrice(lastPrice[0]);
System.out.println();
lastPrice[0] = price[0];

当我偶然发现这个问题时,我只是通过构造函数将对象传递给内部类。如果需要传递原语或不可变对象(如本例),则需要包装器类。

编辑:实际上,我根本不使用匿名类,而是一个适当的子类:

public class PriceData {
        private double lastPrice = 0;
        private double price = 0;

        public void setlastPrice(double lastPrice) {
            this.lastPrice = lastPrice;
        }

        public double getLastPrice() {
            return lastPrice;
        }

        public void setPrice(double price) {
            this.price = price;
        }

        public double getPrice() {
            return price;
        }
    }

    public class PriceTimerTask extends TimerTask {
        private PriceData priceData;
        private Price priceObject;

        public PriceTimerTask(PriceData priceData, Price priceObject) {
            this.priceData = priceData;
            this.priceObject = priceObject;
        }

        public void run() {
            priceData.setPrice(priceObject.getNextPrice(lastPrice));
            System.out.println();
            priceData.setLastPrice(priceData.getPrice());

        }
    }

    public static void main(String args[]) {

        int period = 2000;
        int delay = 2000;

        PriceData priceData = new PriceData();
        Price priceObject = new Price();

        Timer timer = new Timer();

        timer.scheduleAtFixedRate(new PriceTimerTask(priceData, priceObject), delay, period);
    }

我注意到一个没有提到的解决方案(除非我错过了,如果我错过了,请纠正我)是使用类变量。尝试在一个方法中运行一个新线程时遇到了这个问题:new thread (){Do Something}。

从下面调用doSomething()将工作。你不一定要声明它为final,只需要改变变量的作用域,这样它就不会在内部类之前被收集。当然,除非您的流程非常庞大,改变范围可能会产生某种冲突。我不想让我的变量成为final,因为它绝不是final/constant。

public class Test
{

    protected String var1;
    protected String var2;

    public void doSomething()
    {
        new Thread()
        {
            public void run()
            {
                System.out.println("In Thread variable 1: " + var1);
                System.out.println("In Thread variable 2: " + var2);
            }
        }.start();
    }

}