Cannot refer to a non-final variable inside an inner class defined in a different method

前端 未结 20 2201
一向
一向 2020-11-21 05:04

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

相关标签:
20条回答
  • 2020-11-21 05:45

    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);
        }
    
    0 讨论(0)
  • 2020-11-21 05:46

    Can you make lastPrice, priceObject, and price fields of the anonymous inner class?

    0 讨论(0)
  • 2020-11-21 05:48

    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.

    0 讨论(0)
  • 2020-11-21 05:49

    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 lastPrice and price), or don't use an anonymous class.

    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 lastPrice and price variable which is to declare it like so

    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];
    
    0 讨论(0)
  • 2020-11-21 05:50

    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.

    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.

    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);
    }
    
    0 讨论(0)
  • 2020-11-21 05:50

    I just wrote something to handle something along the authors intention. I found the best thing to do was to let the constructor take all 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

    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

        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));
            }
        });
    
    0 讨论(0)
提交回复
热议问题