Java 无法在其他方法中定义的内部类中引用非final变量
编辑: 我需要更改几个变量的值,因为它们在计时器上运行多次。我需要通过计时器在每次迭代中不断更新值。我无法将值设置为final,因为这将阻止我更新值,但是我遇到了我在以下初始问题中描述的错误: 我之前写过以下内容: 我得到的错误是“不能引用在不同方法中定义的内部类中的非最终变量” 这种情况发生在双重价格和价格对象上。你知道我为什么会遇到这个问题吗。我不明白为什么我需要有一个最终的声明。另外,如果你能看到我正在努力做什么,我必须做什么才能绕过这个问题Java 无法在其他方法中定义的内部类中引用非final变量,java,methods,declaration,final,Java,Methods,Declaration,Final,编辑: 我需要更改几个变量的值,因为它们在计时器上运行多次。我需要通过计时器在每次迭代中不断更新值。我无法将值设置为final,因为这将阻止我更新值,但是我遇到了我在以下初始问题中描述的错误: 我之前写过以下内容: 我得到的错误是“不能引用在不同方法中定义的内部类中的非最终变量” 这种情况发生在双重价格和价格对象上。你知道我为什么会遇到这个问题吗。我不明白为什么我需要有一个最终的声明。另外,如果你能看到我正在努力做什么,我必须做什么才能绕过这个问题 因为如果变量不是final,这会让人困惑,因为
因为如果变量不是final,这会让人困惑,因为对它的更改不会在匿名类中得到 只需将变量“price”和“lastPrice”设为最终值 --编辑 很显然,在函数中也不需要为它们赋值。您将需要新的局部变量。无论如何,我怀疑现在有人给了你一个更好的答案。Java不支持true,尽管使用像您在这里使用的匿名类(
new TimerTask(){…}
)看起来像是一种闭包
编辑-见下面的评论-正如KeeperOfTheSoul所指出的,以下不是正确的解释
这就是它不起作用的原因:
变量lastPrice
和price是main()方法中的局部变量。使用匿名类创建的对象可能会一直持续到main()
方法返回之后
当main()
方法返回时,局部变量(如lastPrice
和price
)将从堆栈中清除,因此它们在main()
返回后不再存在
但是匿名类对象引用这些变量。如果匿名类对象在清理变量后试图访问这些变量,那么事情就会变得非常糟糕
通过使lastPrice
和price
final
,它们实际上不再是变量,而是常量。然后,编译器就可以用常量的值(当然是在编译时)替换匿名类中使用的lastPrice
和price
,这样您就不会再有访问不存在的变量的问题了
其他支持闭包的编程语言通过特别处理这些变量来实现这一点——确保它们在方法结束时不会被破坏,这样闭包仍然可以访问这些变量
@安克尔:你可以这样做:
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);
}
为了避免匿名委托引用的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并调用。从计时器中勾选
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),或者不要使用匿名类 因此,您的选项是创建一个实际的内部类,您可以在其中传入变量并以正常方式使用它们 或: 有一个快速(在我看来是丑陋的)黑客为您的最后一个价格和价格变量,这是宣布它这样
final double lastPrice[1];
final double price[1];
在匿名类中,可以这样设置值
price[0] = priceObject.getNextPrice(lastPrice[0]);
System.out.println();
lastPrice[0] = price[0];
您可以将
lastPrice
、priceObject
和price
字段设置为匿名内部类吗?您不能引用非最终变量,因为Java语言规范这么说。从8.1.3开始:“在内部类中使用但未声明的任何局部变量、形式方法参数或异常处理程序参数必须声明为final。”
我只能看到你的部分代码——根据我的说法,安排局部变量的修改是一个奇怪的想法。离开函数时,局部变量不再存在。也许类的静态字段会更好?已经提供了很好的解释,解释了为什么您无法完成您试图完成的任务。作为解决方案,可以考虑:
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 );
}
}
看起来您可能可以做一个更好的设计,但是想法是您可以将更新的变量分组到一个不变的类引用中。当我偶然发现这个问题时,我只是通过构造函数将对象传递给内部类。如果需要传递原语或不可变对象(如本例中所示),则需要一个包装器类 编辑:实际上,我根本不使用匿名类,而是使用一个适当的子类:
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);
}
如果要求为final的变量不能为final,那么您可以将该变量的值分配给另一个变量,并将其设为final,这样您就可以使用它了。我刚刚写了一些东西来处理作者意图中的一些东西。 我发现最好的办法是让构造函数获取所有对象,然后在实现的方法中使用该构造函数对象 但是,如果您正在编写一个通用接口类,那么您必须传递一个对象,或者最好是一个对象列表。这可以通过Object[]或者更好的,Object…来完成,因为它更容易调用 请看下面我的示例
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;
}
}
List lst=new ArrayList();
第1条添加(“1”);
第1条添加(“2”);
SomeAbstractClass p=新的SomeAbstractClass(lst,“另一个参数”,20,true){
公开作废执行(){
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;
}
}
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));
}
});
...
final SettableFuture<Integer> myvalue = SettableFuture<Integer>.create();
...
someclass.run(new Runnable(){
public void run(){
...
myvalue.set(value);
...
}
}
return myvalue.get();
public class WorkerService extends Service {
Worker _worker;
ExecutorService _executorService;
ScheduledExecutorService _scheduledStopService;
TextView _statusTextView;
@Override
public void onCreate() {
_worker = new Worker(this);
_worker.monitorGpsInBackground();
// To get a thread pool service containing merely one thread
_executorService = Executors.newSingleThreadExecutor();
// schedule something to run in the future
_scheduledStopService = Executors.newSingleThreadScheduledExecutor();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
ServiceRunnable runnable = new ServiceRunnable(this, startId);
_executorService.execute(runnable);
// the return value tells what the OS should
// do if this service is killed for resource reasons
// 1. START_STICKY: the OS restarts the service when resources become
// available by passing a null intent to onStartCommand
// 2. START_REDELIVER_INTENT: the OS restarts the service when resources
// become available by passing the last intent that was passed to the
// service before it was killed to onStartCommand
// 3. START_NOT_STICKY: just wait for next call to startService, no
// auto-restart
return Service.START_NOT_STICKY;
}
@Override
public void onDestroy() {
_worker.stopGpsMonitoring();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
class ServiceRunnable implements Runnable {
WorkerService _theService;
int _startId;
String _statusMessage;
public ServiceRunnable(WorkerService theService, int startId) {
_theService = theService;
_startId = startId;
}
@Override
public void run() {
_statusTextView = MyActivity.getActivityStatusView();
// get most recently available location as a latitude /
// longtitude
Location location = _worker.getLocation();
updateStatus("Starting");
// convert lat/lng to a human-readable address
String address = _worker.reverseGeocode(location);
updateStatus("Reverse geocoding");
// Write the location and address out to a file
_worker.save(location, address, "ResponsiveUx.out");
updateStatus("Done");
DelayedStopRequest stopRequest = new DelayedStopRequest(_theService, _startId);
// schedule a stopRequest after 10 seconds
_theService._scheduledStopService.schedule(stopRequest, 10, TimeUnit.SECONDS);
}
void updateStatus(String message) {
_statusMessage = message;
if (_statusTextView != null) {
_statusTextView.post(new Runnable() {
@Override
public void run() {
_statusTextView.setText(_statusMessage);
}
});
}
}
}
Double price;
public static void main(String []args(){
--------
--------
}
public class EnclosingClass {
public void someMethod() {
String shared = "hello";
new Thread() {
public void run() {
// this is not valid, won't compile
System.out.println(shared); // this instance expects shared to point to the reference where the String object "hello" lives in heap
}
}.start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
}
public void someMethod() {
String shared = "hello";
new EnclosingClass$1(shared).start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
public class EnclosingClass$1 extends Thread {
String shared;
public EnclosingClass$1(String shared) {
this.shared = shared;
}
public void run() {
System.out.println(shared);
}
}
public class EnclosingClass {
String shared = "hello";
public void someMethod() {
new Thread() {
public void run() {
System.out.println(shared); // this is perfectly valid
}
}.start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
}
public void someMethod() {
new EnclosingClass$1(this).start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
public class EnclosingClass$1 extends Thread {
EnclosingClass enclosing;
public EnclosingClass$1(EnclosingClass enclosing) {
this.enclosing = enclosing;
}
public void run() {
System.out.println(enclosing.shared);
}
}
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();
}
}
public class Outer{
public static void main(String[] args){
Outer o = new Outer();
o.m1();
o=null;
}
public void m1(){
//int x = 10;
class Inner{
Thread t = new Thread(new Runnable(){
public void run(){
for(int i=0;i<10;i++){
try{
Thread.sleep(2000);
}catch(InterruptedException e){
//handle InterruptedException e
}
System.out.println("Thread t running");
}
}
});
}
new Inner().t.start();
System.out.println("m1 Completes");
}
}