Java 无法在其他方法中定义的内部类中引用非final变量

Java 无法在其他方法中定义的内部类中引用非final变量,java,methods,declaration,final,Java,Methods,Declaration,Final,编辑: 我需要更改几个变量的值,因为它们在计时器上运行多次。我需要通过计时器在每次迭代中不断更新值。我无法将值设置为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");
    }
}