Kann nicht auf eine nicht endgültige Variable innerhalb einer inneren Klasse verweisen, die in einer anderen Methode definiert wurde


247

Bearbeitet: Ich muss die Werte mehrerer Variablen ändern, da sie mehrmals über einen Timer ausgeführt werden. Ich muss die Werte bei jeder Iteration durch den Timer aktualisieren. Ich kann die Werte nicht auf final setzen, da dies mich daran hindert, die Werte zu aktualisieren. Es wird jedoch der Fehler angezeigt, den ich in der ersten Frage unten beschrieben habe:

Ich hatte vorher geschrieben, was unten steht:

Ich erhalte die Fehlermeldung "kann nicht auf eine nicht endgültige Variable innerhalb einer inneren Klasse verweisen, die in einer anderen Methode definiert wurde".

Dies geschieht für den doppelten Preis und den Preis priceObject. Wissen Sie, warum ich dieses Problem bekomme? Ich verstehe nicht, warum ich eine endgültige Erklärung haben muss. Auch wenn Sie sehen können, was ich versuche zu tun, was muss ich tun, um dieses Problem zu umgehen.

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

Ich frage, wie ich eine Variable in einem Timer erhalte, die ich kontinuierlich aktualisieren kann.
Ankur

1
@Ankur: Die einfache Antwort lautet "Nein". Sie können den gewünschten Effekt jedoch mit einer inneren Klasse erzielen. siehe @ petercardonas Antwort.
Stephen C

Antworten:


197

Java unterstützt keine echten Schließungen , obwohl die Verwendung einer anonymen Klasse wie hier ( new TimerTask() { ... }) wie eine Art Schließung aussieht.

Bearbeiten - Siehe die Kommentare unten - das Folgende ist keine korrekte Erklärung, wie KeeperOfTheSoul hervorhebt.

Deshalb funktioniert es nicht:

Die Variablen lastPriceund der Preis sind lokale Variablen in der main () -Methode. Das Objekt, das Sie mit der anonymen Klasse erstellen, kann bis zur main()Rückkehr der Methode gültig sein.

Wenn die main()Methode zurückgegeben wird, werden lokale Variablen (wie lastPriceund price) aus dem Stapel bereinigt, sodass sie nach der main()Rückgabe nicht mehr vorhanden sind .

Das anonyme Klassenobjekt verweist jedoch auf diese Variablen. Es würde furchtbar schief gehen, wenn das anonyme Klassenobjekt versucht, auf die Variablen zuzugreifen, nachdem sie bereinigt wurden.

Indem sie lastPriceund machen price final, sind sie nicht mehr wirklich Variablen, sondern Konstanten. Der Compiler kann dann einfach die Verwendung von lastPriceund pricein der anonymen Klasse durch die Werte der Konstanten ersetzen (natürlich zur Kompilierungszeit), und Sie haben kein Problem mehr mit dem Zugriff auf nicht vorhandene Variablen.

Andere Programmiersprachen, die Closures unterstützen, behandeln diese Variablen speziell, indem sie sicherstellen, dass sie beim Beenden der Methode nicht zerstört werden, damit der Closure weiterhin auf die Variablen zugreifen kann.

@ Ankur: Du könntest das machen:

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

34
Nicht ganz richtig, Java generiert Captures für die betreffenden Variablen, um deren Laufzeitwerte zu erfassen. Sie wollten lediglich einen seltsamen Nebeneffekt vermeiden, der in .Net möglich ist. Wenn Sie den Wert im Delegaten erfassen, ändern Sie den Wert Wert in der äußeren Methode, und jetzt sieht der Delegat den neuen Wert unter stackoverflow.com/questions/271440/c-captured-variable-in-loop für das C # -Beispiel dieses Verhaltens, das Java vermeiden möchte.
Chris Chilvers

14
Das ist kein "seltsamer Nebeneffekt", sondern das normale Verhalten, das Menschen erwarten würden - und das Java nicht liefern kann, weil es keine Captures generiert. Um dieses Problem zu umgehen, müssen lokale Variablen, die in einer anonymen Klasse verwendet werden, endgültig sein.
Michael Borgwardt

12
Jesper, du solltest wahrscheinlich die falschen Teile deiner Antworten herausarbeiten, anstatt nur eine Nachricht zu erhalten, die besagt, dass das oben Gesagte falsch ist.
James McMahon

19
Java unterstützt tatsächlich keine Schließungen. Sprachen, die das Schließen unterstützen, speichern dazu die gesamte lokale Umgebung (dh die im aktuellen Stapelrahmen definierten lokalen Variablen) als Heap-Objekt. Java hat keine Unterstützung dafür (die Sprachdesigner wollten es implementieren, hatten aber keine Zeit mehr). Als Problemumgehung werden bei jeder Instanziierung einer lokalen Klasse die Werte aller lokalen Variablen, auf die sie verweist, auf den Heap kopiert . Die JVM kann die Werte dann jedoch nicht mit den lokalen Variablen synchron halten, weshalb sie endgültig sein müssen.
Taymon

64
Diese Antwort ist jetzt völlig verwirrend, da es niemanden namens "KeeperOfTheSoul" gibt, der dies kommentiert hat. Die Antwort sollte überarbeitet werden.
Adam Parkin

32

Um seltsame Nebenwirkungen bei Schließungen in Java-Variablen zu vermeiden, auf die von einem anonymen Delegaten verwiesen wird, muss sie als endgültig markiert werden. Um auf lastPricedie Timer-Aufgabe zu verweisen und sie zu bewerten, müssen sie als endgültig markiert werden.

Dies funktioniert offensichtlich nicht für Sie, da Sie sie ändern möchten. In diesem Fall sollten Sie versuchen, sie in einer Klasse zu kapseln.

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

Jetzt erstelle einfach ein neues Foo als final und rufe .tick vom Timer auf.

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

1
oder Sie können einfach Foo Runnable implementieren ..?
vidstige

18

Sie können nur dann auf endgültige Variablen aus der enthaltenen Klasse zugreifen, wenn Sie eine anonyme Klasse verwenden. Daher müssen Sie die verwendeten Variablen als final deklarieren (was für Sie keine Option ist, da Sie lastPrice und price ändern ) oder keine anonyme Klasse verwenden.

Sie können also eine tatsächliche innere Klasse erstellen, in der Sie die Variablen übergeben und auf normale Weise verwenden können

oder:

Es gibt einen schnellen (und meiner Meinung nach hässlichen) Hack für Ihre letzte Preis- und Preisvariable , der darin besteht, dies so zu deklarieren

final double lastPrice[1];
final double price[1];

und in Ihrer anonymen Klasse können Sie den Wert so einstellen

price[0] = priceObject.getNextPrice(lastPrice[0]);
System.out.println();
lastPrice[0] = price[0];

14

Gute Erklärungen dafür, warum Sie nicht das tun können, was Sie versuchen, bereits bereitgestellt. Betrachten Sie als Lösung vielleicht:

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

Scheint, als könnten Sie wahrscheinlich ein besseres Design als das machen, aber die Idee ist, dass Sie die aktualisierten Variablen in einer Klassenreferenz gruppieren könnten, die sich nicht ändert.


11

Bei anonymen Klassen deklarieren Sie tatsächlich eine "namenlose" verschachtelte Klasse. Für verschachtelte Klassen generiert der Compiler eine neue eigenständige öffentliche Klasse mit einem Konstruktor, der alle von ihm verwendeten Variablen als Argumente verwendet (für "benannte" verschachtelte Klassen ist dies immer eine Instanz der ursprünglichen / umschließenden Klasse). Dies geschieht, weil die Laufzeitumgebung keine Vorstellung von verschachtelten Klassen hat. Daher muss eine (automatische) Konvertierung von einer verschachtelten in eine eigenständige Klasse erfolgen.

Nehmen Sie diesen Code zum Beispiel:

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

Das wird nicht funktionieren, denn das macht der Compiler unter der Haube:

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

Die ursprüngliche anonyme Klasse wird durch eine eigenständige Klasse ersetzt, die der Compiler generiert (Code ist nicht genau, sollte Ihnen aber eine gute Idee geben):

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

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

Wie Sie sehen können, enthält die eigenständige Klasse einen Verweis auf das gemeinsam genutzte Objekt. Denken Sie daran, dass alles in Java als Wert übergeben wird. Selbst wenn die Referenzvariable 'shared' in EnclosingClass geändert wird, wird die Instanz, auf die sie verweist, nicht geändert und alle anderen Referenzvariablen, die darauf verweisen (wie die in der anonymen Klasse: Enclosing $ 1), werden dies nicht bemerken. Dies ist der Hauptgrund, warum der Compiler Sie zwingt, diese "gemeinsam genutzten" Variablen als endgültig zu deklarieren, damit diese Art von Verhalten nicht in Ihren bereits ausgeführten Code gelangt.

Dies geschieht nun, wenn Sie eine Instanzvariable in einer anonymen Klasse verwenden (dies sollten Sie tun, um Ihr Problem zu lösen, Ihre Logik auf eine "Instanz" -Methode oder einen Konstruktor einer Klasse zu verschieben):

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

Dies lässt sich gut kompilieren, da der Compiler den Code so ändert, dass die neu generierte Klasse Enclosing $ 1 einen Verweis auf die Instanz von EnclosingClass enthält, in der sie instanziiert wurde (dies ist nur eine Darstellung, sollte Sie aber zum Laufen bringen):

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

Wenn die Referenzvariable 'shared' in EnclosingClass neu zugewiesen wird und dies vor dem Aufruf von Thread # run () geschieht, wird "other hello" zweimal gedruckt, da jetzt die EnclosingClass $ 1 # -Einschließungsvariable eine Referenz behält für das Objekt der Klasse, in der es deklariert wurde, sodass Änderungen an einem Attribut für dieses Objekt für Instanzen von EnclosingClass $ 1 sichtbar sind.

Weitere Informationen zu diesem Thema finden Sie in diesem hervorragenden Blog-Beitrag (nicht von mir verfasst): http://kevinboone.net/java_inner.html


Was ist, wenn die lokale Variable 'shared' ein veränderbares Objekt ist? Laut Ihrer Erklärung hilft es auch nicht, "endgültig" zu erklären, oder?
Sactiw

Wenn Sie "freigegeben" als endgültig deklarieren, können Sie den Status des Objekts ändern, auf das die endgültige Variable verweist. In diesem Beispiel funktioniert dies jedoch nicht, da Sie den Wert der "freigegebenen" Variablen (die) nicht ändern können ist das, was das OP wollte), Sie können es in anonymen Klassen verwenden, aber sein Wert ändert sich nicht (weil es als endgültig deklariert ist). Es ist wichtig, den Unterschied zwischen Variablen und den tatsächlichen Werten zu beachten, die sie enthalten (dies können Grundelemente oder Verweise auf Objekte im Heap sein).
Emerino

>>> aber sein Wert ändert sich nicht Ich denke, Sie verpassen den Punkt, dh wenn die endgültige Referenzvariable auf ein veränderliches Objekt zeigt, kann sie trotzdem aktualisiert werden. Die anonyme Klasse erstellt jedoch die flache Kopie, sodass Änderungen in der anonymen geändert werden Klasse. Mit anderen Worten, der Zustand ist synchron, was hier erwünscht ist. Hier muss OP in der Lage sein, die gemeinsam genutzte Variable (den primitiven Typ) zu ändern, und um dies zu erreichen, muss OP den Wert unter ein veränderliches Objekt einschließen und dieses veränderbare Objekt gemeinsam nutzen.
Sactiw

1
Natürlich kann das OP den benötigten Wert unter ein veränderliches Objekt packen, die Variable als final deklarieren und stattdessen verwenden. Er kann jedoch die Verwendung eines zusätzlichen Objekts vermeiden, indem er die Variable als Attribut der aktuellen Klasse deklariert (wie in der Antwort angegeben und erläutert). Das Erzwingen veränderlicher Objekte (z. B. die Verwendung von Arrays, um den Wert einer gemeinsam genutzten Variablen ändern zu können) ist keine gute Idee.
Emerino

7

Wenn ich auf dieses Problem stoße, übergebe ich die Objekte einfach über den Konstruktor an die innere Klasse. Wenn ich Grundelemente oder unveränderliche Objekte übergeben muss (wie in diesem Fall), wird eine Wrapper-Klasse benötigt.

Bearbeiten: Eigentlich verwende ich überhaupt keine anonyme Klasse, sondern eine richtige Unterklasse:

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

2

Sie können nicht auf nicht endgültige Variablen verweisen, da dies in der Java-Sprachspezifikation angegeben ist. Ab 8.1.3:
"Alle lokalen Variablen, formalen Methodenparameter oder Ausnahmebehandlungsparameter, die in einer inneren Klasse verwendet, aber nicht deklariert werden, müssen als endgültig deklariert werden." Ganzer Absatz.
Ich kann nur einen Teil Ihres Codes sehen - meiner Meinung nach ist es eine seltsame Idee, Änderungen an lokalen Variablen zu planen. Lokale Variablen existieren nicht mehr, wenn Sie die Funktion verlassen. Vielleicht wären statische Felder einer Klasse besser?


2

Ich habe gerade etwas geschrieben, um etwas in der Absicht des Autors zu handhaben . Ich fand es am besten, den Konstruktor alle Objekte nehmen zu lassen und dann in Ihrer implementierten Methode diese Konstruktorobjekte zu verwenden.

Wenn Sie jedoch eine generische Schnittstellenklasse schreiben, müssen Sie ein Objekt oder besser eine Liste von Objekten übergeben. Dies könnte mit Object [] oder noch besser mit Object ... geschehen, da es einfacher ist, es aufzurufen.

Siehe mein Beispiel unten.

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

}

In diesem Beitrag finden Sie Informationen zu Java-Schließungen, die dies sofort unterstützen: http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html

Version 1 unterstützt das Übergeben nicht endgültiger Schließungen mit 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));
        }
    });

2

Wenn Sie einen Wert in einem Methodenaufruf innerhalb einer anonymen Klasse ändern möchten, ist dieser "Wert" tatsächlich ein Future. Wenn Sie also Guava verwenden, können Sie schreiben

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

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

 return myvalue.get();

2

Eine Lösung, die mir aufgefallen ist, wird nicht erwähnt (es sei denn, ich habe sie verpasst, wenn ich mich korrigiert habe), ist die Verwendung einer Klassenvariablen. Beim Versuch, einen neuen Thread innerhalb einer Methode auszuführen, ist dieses Problem aufgetreten : new Thread(){ Do Something }.

Das Anrufen doSomething()über Folgendes funktioniert. Sie müssen es nicht unbedingt deklarieren, sondern finalmüssen nur den Umfang der Variablen ändern, damit sie nicht vor der inneren Klasse erfasst wird. Dies gilt nur, wenn Ihr Prozess natürlich sehr umfangreich ist und eine Änderung des Bereichs zu Konflikten führen kann. Ich wollte meine Variable nicht endgültig machen, da sie in keiner Weise endgültig / konstant war.

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

}

1

Wenn die Variable, die endgültig sein muss, nicht endgültig sein kann, können Sie den Wert der Variablen einer anderen Variablen zuweisen und DIESE endgültig machen, damit Sie sie stattdessen verwenden können.


1

Verwenden Sie ClassName.this.variableName, um auf die nicht endgültige Variable zu verweisen


1

Sie können die Variable einfach außerhalb der äußeren Klasse deklarieren. Danach können Sie die Variable innerhalb der inneren Klasse bearbeiten. Ich habe manchmal ähnliche Probleme beim Codieren in Android, daher deklariere ich die Variable als global und es funktioniert für mich.


Dies beantwortet die Frage nicht wirklich ... Deshalb werden Sie herabgestimmt.
Stuart Siegler

0

Können Sie machen lastPrice, priceObjectund priceFelder der anonymen inneren Klasse?


0

Die Hauptsorge ist, ob eine Variable innerhalb der anonymen Klasseninstanz zur Laufzeit aufgelöst werden kann. Es ist kein Muss, eine Variable endgültig zu machen, solange garantiert ist, dass sich die Variable innerhalb des Laufzeitbereichs befindet. Sehen Sie sich beispielsweise die beiden Variablen _statusMessage und _statusTextView in der updateStatus () -Methode an.

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

                }

            });
        }
    }

}

0

Was für mich funktioniert hat, ist nur die Variable außerhalb dieser Funktion von Ihnen zu definieren.

Kurz vor der Hauptfunktion deklarieren dh

Double price;
public static void main(String []args(){
--------
--------
}

Das funktioniert nicht. Sie deklarieren eine Instanzvariable. Um sie zu verwenden, müssen Sie eine Instanz in Ihrer Hauptmethode erstellen. Sie sollten entweder spezifischer sein oder einfach den statischen Modifikator zur Variablen 'price' hinzufügen.
Emerino

0

Deklarieren Sie die Variable als statisch und referenzieren Sie sie in der erforderlichen Methode mit className.variable


Non-static parameter cannot be referenced from a static context
Stephen

@Shweta lokale Variablen und Methodenparameter können nicht als 'statisch' deklariert werden. Außerdem geht es um die Art und Weise, wie sie implementiert wurden, damit Klassen innerhalb von Methoden (lokale anonyme Klassen) auch nach der Methode weiterhin auf lokale Variablen und Methodenparameter zugreifen können ist zurückgekehrt, dh es erstellt ihre 'endgültigen' Kopien und verwendet sie als Instanzvariablen.
Sactiw

0

Nur eine andere Erklärung. Betrachten Sie dieses Beispiel unten

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

Hier wird die Ausgabe sein

m1 wird abgeschlossen

Thread t läuft

Thread t läuft

Thread t läuft

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

Jetzt ist die Methode m1 () abgeschlossen und wir weisen null die Referenzvariable o zu. Jetzt ist das äußere Klassenobjekt für die GC berechtigt, aber das innere Klassenobjekt ist noch vorhanden, das eine (Has-A) Beziehung zum laufenden Thread-Objekt hat. Ohne vorhandenes Outer-Class-Objekt gibt es keine Chance für eine vorhandene m1 () -Methode und ohne vorhandene m1 () -Methode gibt es keine Chance für eine vorhandene lokale Variable. Wenn Inner Class Object jedoch die lokale Variable der m1 () -Methode verwendet, ist alles selbsterklärend .

Um dies zu lösen, müssen wir eine Kopie der lokalen Variablen erstellen und dann mit dem Objekt der inneren Klasse in den Heap kopieren, was Java nur für die endgültige Variable tut, da sie nicht wirklich variabel sind, sondern wie Konstanten (alles geschieht nur zur Kompilierungszeit) nicht zur Laufzeit).


-1

Um das oben genannte Problem zu lösen, treffen verschiedene Sprachen unterschiedliche Entscheidungen.

Für Java entspricht die Lösung dem, was wir in diesem Artikel sehen.

Für C # besteht die Lösung darin, Nebenwirkungen zuzulassen, und die Erfassung als Referenz ist die einzige Option.

Für C ++ 11 besteht die Lösung darin, dem Programmierer die Entscheidung zu ermöglichen. Sie können wählen, ob sie nach Wert oder nach Referenz erfassen möchten. Bei der Erfassung nach Wert würden keine Nebenwirkungen auftreten, da die referenzierte Variable tatsächlich unterschiedlich ist. Bei der Erfassung als Referenz können Nebenwirkungen auftreten, die der Programmierer jedoch erkennen sollte.


-2

Weil es verwirrend ist, wenn die Variable nicht endgültig ist, da die Änderungen nicht in der anonymen Klasse übernommen werden.

Machen Sie einfach die Variablen 'price' und 'lastPrice' endgültig.

- Bearbeiten

Hoppla, und Sie müssen sie natürlich auch in Ihrer Funktion nicht zuweisen. Sie benötigen neue lokale Variablen. Wie auch immer, ich vermute, jemand hat Ihnen inzwischen eine bessere Antwort gegeben.


2
Es ist nicht nur verwirrend - es ist geradezu falsch, daher lässt der Compiler es nicht zu.
Chii

Aber wie ändere ich dann die Werte, wenn ich muss?
Ankur

Nicht nur, weil es verwirrend ist; Dies liegt daran, dass Java keine Schließungen unterstützt. Siehe meine Antwort unten. @Ankur: Sie können die Variablen zu Mitgliedsvariablen des anonymen Klassenobjekts anstelle der lokalen Variablen in main () machen.
Jesper

Er modifiziert sie, so dass sie nicht endgültig sein können.
Robin

Wenn Preis und letzter Preis endgültig wären, würden die ihnen zugewiesenen Zuordnungen nicht kompiliert.
Greg Mattes
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.