Java 不能在不同方法中定义的内部类中引用非最终变量
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1299837/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Cannot refer to a non-final variable inside an inner class defined in a different method
提问by Ankur
Edited: I need to change the values of several variables as they run several times thorugh a timer. I need to keep updating the values with every iteration through the timer. I cannot set the values to final as that will prevent me from updating the values however I am getting the error I describe in the initial question below:
编辑:我需要更改几个变量的值,因为它们通过计时器运行多次。我需要通过计时器在每次迭代中不断更新值。我无法将值设置为最终值,因为这会阻止我更新值,但是我收到了我在下面的初始问题中描述的错误:
I had previously written what is below:
我之前写过以下内容:
I am getting the error "cannot refer to a non-final variable inside an inner class defined in a different method".
This is happening for the double called price and the Price called priceObject. Do you know why I get this problem. I do not understand why I need to have a final declaration. Also if you can see what it is I am trying to do, what do I have to do to get around this problem.
我收到错误“无法引用以不同方法定义的内部类中的非最终变量”。
这发生在称为 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);
}
采纳答案by Jesper
Java doesn't support true closures, even though using an anonymous class like you are using here (new TimerTask() { ... }
) looks like a kind of closure.
Java 不支持真正的闭包,即使使用像您在这里使用的匿名类 ( new TimerTask() { ... }
) 看起来像一种闭包。
edit- See the comments below - the following is not a correct explanation, as KeeperOfTheSoul points out.
编辑-请参阅下面的评论 - 正如 KeeperOfTheSoul 指出的那样,以下不是正确的解释。
This is why it doesn't work:
这就是它不起作用的原因:
The variables lastPrice
and price are local variables in the main() method. The object that you create with the anonymous class might last until after the main()
method returns.
变量lastPrice
和价格是 main() 方法中的局部变量。您使用匿名类创建的对象可能会持续到main()
方法返回之后。
When the main()
method returns, local variables (such as lastPrice
and price
) will be cleaned up from the stack, so they won't exist anymore after main()
returns.
当main()
方法返回时,局部变量(如lastPrice
and price
)会从栈中清除,因此main()
返回后它们将不再存在。
But the anonymous class object references these variables. Things would go horribly wrong if the anonymous class object tries to access the variables after they have been cleaned up.
但是匿名类对象引用了这些变量。如果匿名类对象在变量被清理后试图访问它们,事情就会变得非常糟糕。
By making lastPrice
and price
final
, they are not really variables anymore, but constants. The compiler can then just replace the use of lastPrice
and price
in the anonymous class with the values of the constants (at compile time, of course), and you won't have the problem with accessing non-existent variables anymore.
通过制作lastPrice
and price
final
,它们不再是真正的变量,而是常量。然后编译器可以用常量的值(当然,在编译时)替换匿名类中的lastPrice
和的使用price
,并且您将不再遇到访问不存在的变量的问题。
Other programming languages that do support closures do it by treating those variables specially - by making sure they don't get destroyed when the method ends, so that the closure can still access the variables.
其他支持闭包的编程语言通过特殊处理这些变量来做到这一点 - 通过确保它们在方法结束时不会被破坏,以便闭包仍然可以访问这些变量。
@Ankur: You could do this:
@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);
}
回答by Noon Silk
Because it's confusing if the variable isn't final, as the changes to it won't be picked up in the anonymous class.
因为如果变量不是 final 会令人困惑,因为匿名类不会接收到对它的更改。
Just make the variables 'price' and 'lastPrice' final.
只需将变量 'price' 和 'lastPrice' 设为 final。
-- Edit
- 编辑
Oops, and you'll also need to not assign to them, obviously, in your function. You'll need new local variables. Anyway, I suspect someone has given you a better answer by now.
哎呀,显然,您还需要在您的函数中不要分配给它们。您将需要新的局部变量。无论如何,我怀疑现在有人已经给了你更好的答案。
回答by Chris Chilvers
To avoid strange side-effects with closures in java variables referenced by an anonymous delegate must be marked as final, so to refer to lastPrice
and price within the timer task they need to be marked as final.
为了避免匿名委托引用的 java 变量中的闭包产生奇怪的副作用,必须将其标记为 final,因此要lastPrice
在计时器任务中引用和定价,它们需要标记为 final。
This obviously won't work for you because you wish to change them, in this case you should look at encapsulating them within a class.
这显然对您不起作用,因为您希望更改它们,在这种情况下,您应该考虑将它们封装在一个类中。
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;
}
}
now just create a new Foo as final and call .tick from the timer.
现在只需创建一个新的 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);
}
回答by Robin
You can only access final variables from the containing class when using an anonymous class. Therefore you need to declare the variables being used final (which is not an option for you since you are changing lastPriceand price), or don't use an anonymous class.
使用匿名类时,您只能从包含类访问最终变量。因此,您需要将正在使用的变量声明为 final (由于您正在更改lastPrice和price,因此这不是您的选择),或者不使用匿名类。
So your options are to create an actual inner class, in which you can pass in the variables and use them in a normal fashion
因此,您的选择是创建一个实际的内部类,您可以在其中传入变量并以正常方式使用它们
or:
或者:
There is a quick (and in my opinion ugly) hack for your lastPriceand pricevariable which is to declare it like so
你的lastPrice和price变量有一个快速的(在我看来是丑陋的)hack就是这样声明它
final double lastPrice[1];
final double price[1];
and in your anonymous class you can set the value like this
在您的匿名类中,您可以像这样设置值
price[0] = priceObject.getNextPrice(lastPrice[0]);
System.out.println();
lastPrice[0] = price[0];
回答by Greg Mattes
Can you make lastPrice
, priceObject
, and price
fields of the anonymous inner class?
你能制作匿名内部类的lastPrice
, priceObject
, 和price
字段吗?
回答by Tadeusz Kopec
You cannot refer to non-final variables because Java Language Specification says so. From 8.1.3:
"Any local variable, formal method parameter or exception handler parameter used but not declared in an inner class must be declared final." Whole paragraph.
I can see only part of your code - according to me scheduling modification of local variables is a strange idea. Local variables cease to exist when you leave the function. Maybe static fields of a class would be better?
您不能引用非最终变量,因为 Java 语言规范是这样说的。从 8.1.3 开始:
“任何使用但未在内部类中声明的局部变量、形式方法参数或异常处理程序参数都必须声明为最终的。” 整段。
我只能看到你的部分代码——据我说,对局部变量进行调度修改是一个奇怪的想法。当您离开函数时,局部变量将不复存在。也许类的静态字段会更好?
回答by Peter Cardona
Good explanations for why you can't do what you're trying to do already provided. As a solution, maybe consider:
已经提供了关于为什么你不能做你正在尝试做的事情的很好的解释。作为解决方案,也许可以考虑:
public class foo
{
static class priceInfo
{
public double lastPrice = 0;
public double price = 0;
public Price priceObject = new Price ();
}
public static void main ( String args[] )
{
int period = 2000;
int delay = 2000;
final priceInfo pi = new priceInfo ();
Timer timer = new Timer ();
timer.scheduleAtFixedRate ( new TimerTask ()
{
public void run ()
{
pi.price = pi.priceObject.getNextPrice ( pi.lastPrice );
System.out.println ();
pi.lastPrice = pi.price;
}
}, delay, period );
}
}
Seems like probably you could do a better design than that, but the idea is that you could group the updated variables inside a class reference that doesn't change.
似乎您可以做一个比这更好的设计,但这个想法是您可以将更新的变量分组到一个不会改变的类引用中。
回答by Buhb
When I stumble upon this issue, I just pass the objects to the inner class through the constructor. If I need to pass primitives or immutable objects (as in this case), a wrapper class is needed.
当我偶然发现这个问题时,我只是通过构造函数将对象传递给内部类。如果我需要传递原语或不可变对象(如本例),则需要一个包装类。
Edit: Actually, I don't use an anonymous class at all, but a proper subclass:
编辑:实际上,我根本不使用匿名类,而是使用适当的子类:
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);
}
回答by Thorbj?rn Ravn Andersen
If the variable required to be final, cannot be then you can assign the value of the variable to another variable and make THAT final so you can use it instead.
如果变量要求是最终的,不能是,那么您可以将变量的值分配给另一个变量并使 THAT 成为最终的,以便您可以改用它。
回答by mmm
I just wrote something to handlesomething along the authors intention. I found the best thing to do was to let the constructor takeall the objects and then in your implemented method use that constructor objects.
我刚写的东西手柄的东西沿着作者的意图。我发现最好的办法是让构造函数获取所有对象,然后在您实现的方法中使用该构造函数对象。
However, if you are writing a generic interface class, then you have to pass an Object, or better a list of Objects. This could be done by Object[] or even better, Object ...because it is easier to call.
但是,如果您正在编写通用接口类,那么您必须传递一个对象,或者更好的对象列表。这可以通过对象[],甚至更好,做对象...因为它更容易调用。
See my example piece just below.
请参阅下面的示例作品。
List<String> lst = new ArrayList<String>();
lst.add("1");
lst.add("2");
SomeAbstractClass p = new SomeAbstractClass (lst, "another parameter", 20, true) {
public void perform( ) {
ArrayList<String> lst = (ArrayList<String>)getArgs()[0];
}
};
public abstract class SomeAbstractClass{
private Object[] args;
public SomeAbstractClass(Object ... args) {
this.args = args;
}
public abstract void perform();
public Object[] getArgs() {
return args;
}
}
Please see this post about Java closures that supports this out of the box: http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html
请参阅这篇关于支持开箱即用的 Java 闭包的帖子:http: //mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html
Version 1 supports passing of non-final closures with autocasting:
https://github.com/MSeifeddo/Closure-implementation-for-Java-5-6-and-7/blob/master/org/mo/closure/v1/Closure.java
版本 1 支持通过自动广播传递非最终闭包:https:
//github.com/MSeifeddo/Closure-implementation-for-Java-5-6-and-7/blob/master/org/mo/closure/v1/闭包.java
SortedSet<String> sortedNames = new TreeSet<String>();
// NOTE! Instead of enforcing final, we pass it through the constructor
eachLine(randomFile0, new V1<String>(sortedNames) {
public void call(String line) {
SortedSet<String> sortedNames = castFirst(); // Read contructor arg zero, and auto cast it
sortedNames.add(extractName(line));
}
});