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

20

251

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.

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);
}
Standing answered 19/8, 2009 at 13:12 Comment(4)
What I am asking is, how do I get a variable in a timer that I can continuously update.Standing
@Ankur: the simple answer is "No". But you can achieve the desired effect using an inner class; see @petercardona's answer.Kinney
@StephenC sorry but who is this petercardona now?Freestyle
https://mcmap.net/q/115802/-cannot-refer-to-a-non-final-variable-inside-an-inner-class-defined-in-a-different-method. I'm amazed you needed to ask that ...Kinney
I
199

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.

edit - See the comments below - the following is not a correct explanation, as KeeperOfTheSoul points out.

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.

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.

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.

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:

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);      
}
Isomerous answered 19/8, 2009 at 13:22 Comment(29)
Not quite true, Java does generate captures for the variables in question to capture their run-time values, its just they wanted to avoid a strange side-effect that is possible in .Net where by you capture the value in the delegate, change the value in the outer method, and now the delegate sees the new value see, https://mcmap.net/q/35587/-captured-variable-in-a-loop-in-c for the C# example of this behaviour that Java aims to avoid.Jackijackie
How do you know that Java does generate captures? As far as I know, it isn't necessary to do so if it's required that the local variables should be final, so why would Java generate captures?Isomerous
It captures the value somewhere, even if its just in a private hidden field of the anonymous class, as the actual values can be computed at runtime rather than compile time, which wouldn't be possible with an actual constant.Jackijackie
That's not a "strange side-effect", it's the normal behaviour people would expect - and which Java cannot deliver because it does not generate captures. As a workaround, local variables used in an anonymous class must be final.Mite
True, "constant" is not the correct expression (but neither is IMO "capture"). The Anonymous class object simply gets a copy of the value.Mite
It's amazing the number of people using C# that don't expect that, I guess when Sun were choosing how to capture variables they decided to avoid that by enforcing the variables be final, which is rather annoying and leads to requiring wrapper objects to get the same effect.Jackijackie
Hmmm, you're right. I just made a small test program with a value that is computed at runtime and disassembled it with javap. It looks like the value is stored in a compiler-generated member variable of the anonymous class.Isomerous
Jesper, you should probably edit out the incorrect parts of your answers rather then just having a message saying the above is incorrect.Excelsior
@James If I'd do that the whole discussion in the comments wouldn't be understandable anymore... I'll leave it there in this case with another remark at the top of the text.Isomerous
Java does not, in fact, support closures. Languages that support closures do so by storing the entire local environment (that is, the set of local variables defined in the current stack frame) as a heap object. Java doesn't have support for this (the language designers wanted to implement it but ran out of time), so as a workaround, whenever a local class is instantiated, the values of any local variables which it refers to are copied onto the heap. However, the JVM can't then keep the values in sync with the local variables, which is why they have to be final.Dummy
@Dummy Is there any relevant doc from which I can refer the same(your explanation)?Graz
@Dummy what exactly is "copied onto the heap"? I think we all want to know, whether your object is deep-cloned, or what?Claire
@Claire No objects are copied. The value of each final local variable is stored in the instance of the local class. For non-primitive variables, this value is a reference to an object, not an entire object.Dummy
This answer is completely confusing now that there is nobody named "KeeperOfTheSoul" who has commented to it. The answer should be revised.Hekker
The explanation (or background) for this answer is wrong, stating that the objects created inside a method (local context) are destroyed after the method execution ends (and its context is removed from the stack) is not correct, local VARIABLES will be destroyed (removed from the stack), but the objects they reference (in the case of reference variables) will remain in the heap until the gc runs and there are no more strong references to this object. The reason this actually happens has to do with the way the java compiler works, please see my answer for an explanation.Reveille
@ChrisChilvers - I believe avoiding the "strange side-effect" was the true motivation behind Java's choice. It's not that somehow Java designers were not as smart as designers of those other languages:) Recently for Java 8 the issue was revisited, and mutability is again overruled, because firstly Brian Goetz is obsessed with parallel Stream:) The variable is not even allowed to mutate in its declaring scope (i.e. effective finalness) because that would surprise some other people too. It's final, period, saving a lot of trouble.Glint
If you have lastPrice within the inner class like that, how do you then access it from the outer class. After the timer.scheduleAtFixedRate(new TimerTask{...}), you can't actually access lastPrice, can you?Retired
@Caketray You can't access lastPrice from the outer class. If you need that, then just make a regular class that extends TimerTask instead of an anonymous inner class.Isomerous
@Taymon, that explanation of things is misleading. There is no practical difference between "real" closures and Java's workaround. It's just more convenient to create closures in languages that have better support for them. The final requirement is an inconvenience, nothing more: it doesn't mean the object can't be mutable. A claim to the contrary should demonstrate what's possible with "real" closures that isn't possible in Java's implementation.Overtop
@Isomerous if it is local class. then how the class object could be there even after finishing the main method?Brittne
@UnKnown A local class (defined inside a method) could extend a non-local class, or implement an interface. You return a reference to an instance of the local class out of the method, if the method's return type is the superclass or interface that the local class extends.Isomerous
@Isomerous why java didn't generated a copy of local variable to inner class as final constant?Brittne
How come inner class object still exist if main method returns ?Oldie
@shivaR Because the timer thread is still running and holding a reference to the object.Isomerous
@Dummy you said "However, the JVM can't then keep the values in sync with the local variables, which is why they have to be final." ... by sync you mean if local variable is change later then it should be reflected in their copies as well and since JVM couldn't do that thus it puts the restriction to make it 'final'? (side note: mutable local variable can still be changed but since object is shared on heap thus changes will get reflected so no issues with jvm).Wildfowl
@ChrisChilvers and others... If the user who posted the answer is not willing to edit it to make it correct, someone with knowledge (which there appears to be in the comment thread) should edit the answer instead. We should not have incorrect answers on StackExchange marked as correct. This is why the answer edit feature is open to other members, not just the original author.Transcribe
@Dummy "the language designers wanted to implement it but ran out of time" any references to that? I'm quite interested in the historical background.Freestyle
Based on the long discussion above, I tend to think @ChrisChilvers was KeeperOfTheSoul, whose answer below does directly address the OP's question. For those who are interested why java was designed like this, which is almost completely another question, see Jon Skeet's answer to another question.Freestyle
Unfortunately, I wrote that comment over a decade ago and no longer remember what my source was. Here's a primary source offering a competing explanation: web.archive.org/web/20160717100152/http://article.gmane.org/…Dummy
J
32

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);
}
Jackijackie answered 19/8, 2009 at 13:23 Comment(1)
or you can just led Foo implement Runnable..?Civics
D
19

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];
Dionnadionne answered 19/8, 2009 at 13:24 Comment(0)
H
14

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.

Hom answered 19/8, 2009 at 13:31 Comment(0)
R
11

With anonymous classes, you are actually declaring a "nameless" nested class. For nested classes, the compiler generates a new standalone public class with a constructor that will take all the variables it uses as arguments (for "named" nested classes, this is always an instance of the original/enclosing class). This is done because the runtime environment has no notion of nested classes, so there needs to be a (automatic) conversion from a nested to a standalone class.

Take this code for example:

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

That won't work, because this is what the compiler does under the hood:

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

The original anonymous class is replaced by some standalone class that the compiler generates (code is not exact, but should give you a good idea):

public class EnclosingClass$1 extends Thread {
    String shared;
    public EnclosingClass$1(String shared) {
        this.shared = shared;
    }

    public void run() {
        System.out.println(shared);
    }
}

As you can see, the standalone class holds a reference to the shared object, remember that everything in java is pass-by-value, so even if the reference variable 'shared' in EnclosingClass gets changed, the instance it points to is not modified, and all other reference variables pointing to it (like the one in the anonymous class: Enclosing$1), will not be aware of this. This is the main reason the compiler forces you to declare this 'shared' variables as final, so that this type of behavior won't make it into your already running code.

Now, this is what happens when you use an instance variable inside an anonymous class (this is what you should do to solve your problem, move your logic to an "instance" method or a constructor of a class):

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

This compiles fine, because the compiler will modify the code, so that the new generated class Enclosing$1 will hold a reference to the instance of EnclosingClass where it was instantiated (this is only a representation, but should get you going):

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

Like this, when the reference variable 'shared' in EnclosingClass gets reassigned, and this happens before the call to Thread#run(), you'll see "other hello" printed twice, because now EnclosingClass$1#enclosing variable will keep a reference to the object of the class where it was declared, so changes to any attribute on that object will be visible to instances of EnclosingClass$1.

For more information on the subject, you can see this excelent blog post (not written by me): http://kevinboone.net/java_inner.html

Reveille answered 28/9, 2014 at 21:1 Comment(4)
What if local variable 'shared' is a mutable object? As per your explanation declaring 'final' won't help either, right?Wildfowl
Declaring "shared" as final will allow you to modify the state of the object the final variable references, but for this particular example that won't work because you won't be able to change the value of the "shared" variable (which is what the OP wanted), you will be able to use it inside anonymous classes, but it's value won't change (because it is declared final). It is important to notice the difference between variables and the actual values they hold (which may be primitives or references to objects in heap).Reveille
>>> but it's value won't change I guess you are missing the point i.e. if the final reference variable is pointing to a mutable object it can still be updated, however, anonymous class creates the shallow copy thus changes are reflected in the anonymous class. In other words, state are in sync which is what is desired here. Here, OP needs capability to modify the shared variable (the primitive type) and to achieve that OP will need to wrap the value under a mutable object and shared that mutable object.Wildfowl
Of course the OP can wrap the needed value under a mutable object, declare the variable as final and use that instead. However, he can avoid using an extra object by declaring the variable as an attribute of the current class (as pointed and explained in the answer). Forcing mutable objects (like using arrays just to be able to modify the value of a shared variable) is not a good idea.Reveille
P
7

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);
    }
Pyromagnetic answered 19/8, 2009 at 13:36 Comment(0)
U
2

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?

Unscreened answered 19/8, 2009 at 13:30 Comment(0)
S
2

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));
        }
    });
Subjacent answered 5/10, 2011 at 12:23 Comment(0)
S
2

If you want to change a value in a method call within an anonymous class, that "value" is actually a Future. So, if you use Guava, you can write

...
final SettableFuture<Integer> myvalue = SettableFuture<Integer>.create();
...
someclass.run(new Runnable(){

    public void run(){
        ...
        myvalue.set(value);
        ...
    }
 }

 return myvalue.get();
Sianna answered 16/5, 2013 at 2:54 Comment(0)
D
2

One solution I have noticed isn't mentioned (unless I missed it, if I did please correct me), is the use of a class variable. Ran into this issue attempting to run a new thread within a method: new Thread(){ Do Something }.

Calling doSomething() from the following will work. You do not necessarily have to declare it final, just need to change the scope of the variable so it is not collected before the innerclass. This is unless of course your process is huge and changing the scope might create some sort of conflict. I didn't want to make my variable final as it was in no way a 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();
    }

}
Dyak answered 23/12, 2014 at 18:38 Comment(0)
U
2

you can just declare the variable outside the outer class. After this, you will be able to edit the variable from within the inner class. I sometimes face similar problems while coding in android so I declare the variable as global and it works for me.

Uniseptate answered 2/9, 2015 at 10:6 Comment(1)
This doesn't really answer the question... Its why you're getting downvoted.Galer
H
1

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.

Holmen answered 19/8, 2009 at 13:39 Comment(0)
E
1

use ClassName.this.variableName to reference the non-final variable

Endophyte answered 17/8, 2014 at 23:48 Comment(0)
V
0

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

Visualize answered 19/8, 2009 at 13:29 Comment(0)
W
0

The main concern is whether a variable inside the anonymous class instance can be resolved at run-time. It is not a must to make a variable final as long as it is guaranteed that the variable is inside the run-time scope. For example, please see the two variables _statusMessage and _statusTextView inside updateStatus() method.

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

                }

            });
        }
    }

}
Whipsaw answered 23/5, 2013 at 12:43 Comment(0)
F
0

what worked for me is just define the variable outside this function of your.

Just before main function declare i.e.

Double price;
public static void main(String []args(){
--------
--------
}
Friulian answered 26/5, 2013 at 1:18 Comment(1)
That won't work, you are declaring an instance variable, to use it, you'll need to create an instance inside your main method. You should either be more specific or simply add the static modifier to the 'price' variable.Reveille
A
0

Declare the variable as a static and reference it in the required method using className.variable

Ahoy answered 23/9, 2014 at 5:20 Comment(2)
Non-static parameter cannot be referenced from a static contextUptodate
@Shweta local variables and method parameters can't be declared 'static', moreover, it is about the way it has been implemented to allow classes within methods (local anonymous classes) to continue access to local variables and method parameters even after the method has returned i.e. it makes their 'final' copies and uses them as instance variables.Wildfowl
S
0

Just an another explanation. Consider this example below

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

Here Output will be

m1 Completes

Thread t running

Thread t running

Thread t running

................

Now method m1() completes and we assign reference variable o to null , Now Outer Class Object is eligible for GC but Inner Class Object is still exist who has (Has-A) relationship with Thread object which is running. Without existing Outer class object there is no chance of existing m1() method and without existing m1() method there is no chance of existing its local variable but if Inner Class Object uses the local variable of m1() method then everything is self explanatory.

To solve this we have to create a copy of local variable and then have to copy then into the heap with Inner class object, what java does for only final variable because they are not actually variable they are like constants(Everything happens at compile time only not at runtime).

Stupefy answered 5/7, 2016 at 7:37 Comment(0)
S
-1

To solve the problem above, different languages make different decisions.

for Java, the solution is as what we see in this article.

for C#, the solution is allow side-effects and capture by reference is the only option.

for C++11, the solution is to allow the programmer make the decision. They can choose to capture by value or by reference. If capturing by value, no side-effects would occur because the variable referenced is actually different. If capture by reference, side-effects may occur but the programmer should realize it.

Sianna answered 19/10, 2011 at 2:8 Comment(0)
K
-2

Because it's confusing if the variable isn't final, as the changes to it won't be picked up in the anonymous class.

Just make the variables 'price' and '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.

Kries answered 19/8, 2009 at 13:14 Comment(7)
its not just confusing - its downright incorrect, thus the compiler does not allow it.Succinic
But then how do I change the values when I need to?Standing
Not just because it's confusing; this is because Java doesn't support closures. See my answer below. @Ankur: You could make the variables member variables of the anonymous class object instead of local variables in main().Isomerous
He is modifying them, so they cannot be final.Dionnadionne
If price and lastPrice were final, the assignments to them would not compile.Visualize
Yes, sorry, I missed that, I guess it was implied that he should've declared new local variables.Kries
If Java had better support for closures, then (contrary to this answer as currently written) the changes would be picked up. There are two mostly reasonable interpretations, which means confusion (and that does happen in real examples).Dumdum

© 2022 - 2024 — McMap. All rights reserved.