2009-08-19 23 views
231

Edytowana: Muszę zmienić wartości kilku zmiennych, ponieważ są one uruchamiane kilka razy w stosunku do licznika czasu. Muszę aktualizować wartości przy każdej iteracji przez timer. Nie mogę ustawić wartości końcowe jako że uniemożliwi mi z aktualizacją wartości jednak otrzymuję błąd opiszę w początkowym pytanie poniżej:Nie można odwoływać się do nieostatniej zmiennej wewnątrz klasy wewnętrznej zdefiniowanej w innej metodzie

Wcześniej napisane co jest poniżej:

jestem uzyskanie błędu "nie może odnosić się do nieostatecznej zmiennej wewnątrz klasy wewnętrznej zdefiniowanej w innej metodzie".

Dzieje się tak w przypadku podwójnej ceny i ceny o nazwie priceObject. Czy wiesz, dlaczego mam ten problem? Nie rozumiem, dlaczego potrzebuję ostatecznej deklaracji. Jeśli widzisz, co to jest, co próbuję zrobić, co muszę zrobić, aby obejść ten 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); 
} 
+0

To, o co pytam, to jak uzyskać zmienną w zegarze, którą mogę ciągle aktualizować. – Ankur

+1

@Ankur: prosta odpowiedź brzmi "Nie". Ale możesz osiągnąć pożądany efekt używając wewnętrznej klasy; zobacz odpowiedź @ petercardona. –

Odpowiedz

186

Java nie obsługuje wartości true closures, mimo że używanie anonimowej klasy, takiej jak ta, której używasz tutaj (new TimerTask() { ... }) wygląda na rodzaj zamknięcia.

edytuj - Zobacz komentarze poniżej - poniższe wyjaśnienie nie jest prawidłowe, jak wskazuje KeeperOfTheSoul.

To dlaczego to nie działa:

Zmienne lastPrice i ceny są zmienne lokalne w głównej() metody. Obiekt utworzony przy użyciu anonimowej klasy może potrwać do momentu powrotu metody main().

Kiedy main() metoda powraca, zmienne lokalne (takie jak lastPrice i price) zostaną oczyszczone ze stosu, tak że nie będzie już istnieć po main() zwrotów.

Ale anonimowy obiekt klasy odwołuje się do tych zmiennych. Sprawy potoczyłyby się okropnie, gdyby anonimowy obiekt klasy próbował uzyskać dostęp do zmiennych po ich wyczyszczeniu.

Dokonując lastPrice i pricefinal, nie są to już zmienne, ale stałe. Kompilator może następnie zastąpić użycie klas anonimowych wartościami stałych (oczywiście podczas kompilacji) i nie będzie już problemu z dostępem do nieistniejących zmiennych.

Inne języki programowania, które obsługują zamknięcia, robią to, traktując te zmienne w sposób szczególny - upewniając się, że nie ulegają zniszczeniu, gdy metoda się kończy, tak że zamknięcie może nadal uzyskiwać dostęp do zmiennych.

@Ankur: Można to zrobić:

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

Nie do końca prawda, Java generuje przechwytywanie dla zmiennych, o których mowa, aby uchwycić ich wartości czasu wykonywania, po prostu chcieli uniknąć dziwnego efektu ubocznego, który jest możliwy w .Net gdzie przez przechwycisz wartość w delegacie, zmień wartość w zewnętrznej metodzie, a teraz delegat widzi nową wartość zobacz, http://stackoverflow.com/questions/271440/c-captured-variable-in-loop for przykład C# tego zachowania, którego ma unikać. –

+0

Skąd wiadomo, że Java generuje przechwytywania? O ile wiem, nie jest to konieczne, jeśli wymagane jest, aby zmienne lokalne były ostateczne, dlaczego więc Java generowałaby przechwytywanie? – Jesper

+4

Przechwytuje wartość gdzieś, nawet jeśli jest to tylko w ukrytym polu prywatnej klasy anonimowej, ponieważ rzeczywiste wartości można obliczyć w czasie wykonywania zamiast czasu kompilacji, co nie byłoby możliwe przy rzeczywistej stałej. –

-2

Ponieważ jest to mylące, jeśli zmienna nie jest ostateczna, ponieważ zmiany w nim nie będzie odebrać w anonimowej klasy.

Po prostu ustaw zmienne "price" i "lastPrice" final.

- Edycja

Ups, i będziesz też nie trzeba przypisać do nich, oczywiście, w swojej funkcji. Potrzebujesz nowych zmiennych lokalnych. W każdym razie podejrzewam, że ktoś dał ci teraz lepszą odpowiedź.

+2

to nie tylko mylące - wręcz niepoprawne, a więc kompilator na to nie pozwala. – Chii

+0

Ale jak mogę zmienić wartości, kiedy trzeba? – Ankur

+0

Nie tylko dlatego, że jest mylące; dzieje się tak, ponieważ Java nie obsługuje zamknięć. Zobacz moją odpowiedź poniżej. @Ankur: Można dokonać zmiennych składowych zmiennych anonimowego obiektu klasy zamiast zmiennych lokalnych w main(). – Jesper

30

Aby uniknąć dziwnych efektów ubocznych z zamknięciami w zmiennych Java przywoływanych przez anonimowego delegata musi być oznaczony jako wersja ostateczna, więc odwołać się do lastPrice i cenie w ramach zadanie czasowe muszą być oznaczone jako ostateczne.

To oczywiście nie zadziała dla ciebie, ponieważ chcesz je zmienić, w tym przypadku powinieneś przyjrzeć się enkapsulacji w klasie.

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

teraz po prostu utwórz nowy Foo jako ostateczny i wywołaj .tick z licznika czasu.

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

, czy możesz po prostu poprowadzić narzędzie Foo Runnable ..? – vidstige

15

Dostęp do zmiennych końcowych można uzyskać tylko z klasy zawierającej podczas korzystania z anonimowej klasy. Dlatego musisz zadeklarować, że zmienne są używane jako ostateczne (nie jest to opcja dla Ciebie, ponieważ zmieniasz lastCrice i cena), lub nie używaj anonimowej klasy.

więc opcje są do stworzenia rzeczywistej klasę wewnętrzną, w której można przekazać w zmiennych i wykorzystać je w normalny sposób

czyli

Jest szybkie (i moim zdaniem brzydki) włamać do lastPrice i cen zmiennej, która ma zadeklarować je jak tak

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

w swojej anonimowej klasy można ustawić wartość takiego

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

Czy można lastPrice, priceObject i price pola anonimowej klasy wewnętrznej?

2

Nie można odwoływać się do zmiennych nieostatnich, ponieważ mówi o tym specyfikacja języka Java. Od 8.1.3:
"Każda zmienna lokalna, formalny parametr metody lub parametr obsługi wyjątku użyty, ale nie zadeklarowany w klasie wewnętrznej, musi zostać zadeklarowany jako ostateczny." Whole paragraph. Widzę tylko część kodu - według mnie modyfikacja ustawień lokalnych zmiennych jest dziwnym pomysłem. Zmienne lokalne przestają istnieć po opuszczeniu funkcji. Może statyczne pola klasy będą lepsze?

13

Dobre wyjaśnienie, dlaczego nie możesz zrobić tego, co już chcesz zrobić. Jako rozwiązanie, może rozważyć:

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

Wydaje się, prawdopodobnie można zrobić lepszą konstrukcję niż to, ale chodzi o to, że można grupa zaktualizowane zmienne wewnętrzne odniesienie klasy, że nie zmienia się.

7

Kiedy natknę się na ten problem, po prostu przekazuję obiekty do wewnętrznej klasy za pośrednictwem konstruktora.Jeśli muszę przekazać prymitywy lub obiekty niezmienne (jak w tym przypadku), potrzebna jest klasa otoki.

Edit: Właściwie nie używam anonimową klasę w ogóle, ale odpowiednia podklasa:

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

Jeśli wymagana zmienna będzie ostateczna, nie może być następnie można przypisać wartość zmiennej do innej zmiennej i spraw, aby to było ostateczne, abyś mógł z niego korzystać.

2

Właśnie napisałem coś do uchwyt coś wzdłuż intencji autorów. Znalazłem najlepszą rzeczą do zrobienia było pozwolić konstruktor wziąć wszystkie obiekty, a następnie w zaimplementowanej metody używać tych obiektów konstruktora.

Jednak, jeśli piszesz ogólną klasę interfejsu, musisz przekazać obiekt lub lepiej listę obiektów. Można to zrobić dzięki Object [] lub jeszcze lepiej, Obiekt ..., ponieważ łatwiej jest zadzwonić.

Zobacz mój przykładowy kawałek tuż poniżej.

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

} 

Proszę zobaczyć ten post o zamknięć Java, który obsługuje ten po wyjęciu z pudełka: http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html

Wersja 1 podpory przechodzące zamknięć nieostatecznej z 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)); 
     } 
    }); 
-1

Aby rozwiązać powyższy problem, różne języki podejmują różne decyzje.

dla języka Java, rozwiązanie jest zgodne z tym, co widzimy w tym artykule.

dla języka C#, rozwiązaniem jest dopuszczenie efektów ubocznych i przechwytywanie przez odniesienie jest jedyną opcją.

dla C++ 11, rozwiązaniem jest umożliwienie programistowi podjęcia decyzji. Mogą wybrać przechwytywanie według wartości lub przez odniesienie. Jeśli przechwytywanie przez wartość, nie wystąpią żadne efekty uboczne, ponieważ zmienna, do której się odwołuje, jest rzeczywiście inna. W przypadku przechwytywania przez odniesienie mogą wystąpić efekty uboczne, ale programista powinien je zrealizować.

2

Jeśli chcesz zmienić wartość w wywołaniu metody w ramach anonimowej klasy, ta "wartość" jest w rzeczywistości Future. Tak więc, jeśli używasz guawy, można napisać

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

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

return myvalue.get(); 
0

Głównym problemem jest to, czy zmienna wewnątrz anonimowej instancji klasy mogą być rozwiązane w czasie wykonywania. Nie jest konieczne tworzenie zmiennych końcowych, o ile zagwarantowane jest, że zmienna znajduje się wewnątrz zakresu czasu działania. Na przykład zobacz dwie zmienne _statusMessage i _statusTextView inside metoda updateStatus().

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

to, co dla mnie zadziałało, to po prostu zdefiniuj zmienną poza tą funkcją.

Tuż przed głównym zadeklarować funkcji tj

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

To nie zadziała, deklarujesz zmienną instancji, aby z niej skorzystać, musisz utworzyć instancję wewnątrz swojej głównej metody. Powinieneś być bardziej konkretny lub po prostu dodać modyfikator statyczny do zmiennej "cena". – emerino

1

użytkowania ClassName.this.variableName aby odwołać zakaz końcowego zmienną

0

zadeklarować zmienną jako statyczną i odwoływać się do niej w wymaganej metody wykorzystującej className.variable

+0

'parametr dla statyczny nie można odwoływać się od statycznego context' – surfer190

+0

@Shweta lokalne zmienne i parametry metoda nie może być uznana za„statyczne”, ponadto jest o sposób, w jaki zostały wdrożone w celu umożliwienia zajęcia w ramach metod (Local anonimowy classes), aby kontynuować dostęp do zmiennych lokalnych i parametrów metody nawet po powrocie metody, tj. tworzy ona "końcowe" kopie i wykorzystuje je jako zmienne instancji. – sactiw

8

Z anonimowych klas, są faktycznie uznającej „bezimienny” zagnieżdżone klasy. W przypadku klas zagnieżdżonych kompilator generuje nową autonomiczną klasę publiczną z konstruktorem, który pobiera wszystkie zmienne, których używa jako argumentów (w przypadku "nazwanych" klas zagnieżdżonych jest to zawsze instancja klasy oryginalnej/obejmującej). Dzieje się tak dlatego, że środowisko wykonawcze nie ma pojęcia klas zagnieżdżonych, więc musi istnieć (automatyczna) konwersja z zagnieżdżonej do samodzielnej klasy.

Weźmy na przykład ten kod:

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

To nie będzie działać, bo to co robi kompilator pod maską:

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

Oryginalny klasy anonimowy jest zastąpiony przez jakiś standalone klasa generowana przez kompilator (kod nie jest dokładny, ale powinien dać Ci dobry pomysł):

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

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

Jak widać, klasa autonomiczna zawiera odwołanie do obiektu współużytkowanego, pamiętaj, że wszystko w java jest wartością typu pass-by-value, więc nawet jeśli zmienna referencyjna "shared" w EnclosingClass zostanie zmieniona, instancja, którą wskazuje, nie jest zmodyfikowane i wszystkie inne zmienne wskazujące na nią (takie jak w anonimowej klasie: Enclosing $ 1), nie będą tego świadome. Jest to główny powód, dla którego kompilator zmusza Cię do zadeklarowania tych "współdzielonych" zmiennych jako ostatecznych, aby tego typu zachowanie nie spowodowało przejścia do już działającego kodu.

To właśnie dzieje się, gdy używasz zmiennej instancji wewnątrz anonimowej klasy (to, co powinieneś zrobić, aby rozwiązać swój problem, przenieś swoją logikę do metody "instancji" lub konstruktora klasy):

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

to kompiluje grzywny, ponieważ kompilator będzie zmodyfikować kod, tak aby nowe wygenerowane klasy Zamknięcie 1 $ trzymać referencję do instancji EnclosingClass gdzie została instancja (to tylko przedstawienie, ale powinno ci będzie):

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

W ten sposób, gdy zmienna referencyjna "udostępniona" w EnclosingClass zostanie ponownie przypisana, a dzieje się to przed wywołaniem Thread # run(), zobaczysz "inne powitanie" wydrukowane dwukrotnie, ponieważ teraz EnclosingClass $ 1 # zawiera zmienną zachowa odwołanie do obiektu klasy, w którym zostało zadeklarowane, więc zmiany dowolnego atrybutu na tym obiekcie będą widoczne dla instancji klasy EnclosingClass $ 1.

Aby uzyskać więcej informacji na ten temat można zobaczyć ten excelent blogu (nie napisane przez mnie): http://kevinboone.net/java_inner.html

+1

Troska o komentarz, dlaczego głosowanie w dół? Nie widzę niczego złego w powyższym wyjaśnieniu. – emerino

+0

Co się stanie, jeśli zmienna lokalna "shared" jest zmiennym obiektem? Jak na twoje wyjaśnienie, deklaracja "ostateczna" też nie pomoże, prawda? – sactiw

+0

Zgłoszenie "współdzielonego" jako ostatecznego umożliwi modyfikację stanu obiektu, do którego odnoszą się zmienne końcowe, ale dla tego konkretnego przykładu, który nie zadziała, ponieważ nie będzie można zmienić wartości zmiennej "udostępnionej" (czego właśnie chciał OP), będziesz mógł go używać w anonimowych klasach, ale jego wartość nie zmieni się (ponieważ jest deklarowana jako ostateczna). Ważne jest, aby zauważyć różnicę między zmiennymi a rzeczywistymi wartościami, które posiadają (które mogą być prymitywami lub odniesieniami do obiektów w stercie). – emerino

2

Jednym rozwiązaniem Zauważyłem nie jest wymienione (chyba brakowało mi go, jeśli nie proszę poprawić ja), jest użycie zmiennej klasy. Podjęto ten problem, próbując uruchomić nowy wątek w ramach metody: new Thread(){ Do Something }.

Zadzwoń pod numer doSomething(). Nie musisz koniecznie zadeklarować tego final, wystarczy zmienić zakres zmiennej, aby nie została ona zebrana przed klasą wewnętrzną. Jest tak, o ile oczywiście proces nie jest duży i zmiana zakresu może wywołać pewien konflikt. Nie chciałem zrobić mojego zmiennego finału, ponieważ nie był on w żadnym wypadku końcowy/stały.

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

możesz po prostu zadeklarować zmienną poza zewnętrzną klasą. Po tym będziesz mógł edytować zmienną wewnątrz wewnętrznej klasy. Czasami mam podobne problemy podczas kodowania w Androidzie, więc ogłaszam zmienną jako globalną i działa ona dla mnie.

+0

To tak naprawdę nie jest odpowiedzią na pytanie ... Dlaczego dostajesz zniżkę. –

0

Po prostu inne wyjaśnienie. Rozważmy następujący przykład poniżej

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

Tutaj wyjściowa będzie

m1 Kończy

t nić

Temat t uruchomiony

t nić

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

Teraz metoda m1() kończy się, a my przypisujemy zmienną referencyjną o do wartości null, Teraz obiekt klasy zewnętrznej jest uprawniony do GC, ale obiekt klasy wewnętrznej nadal istnieje i ma relację (z hasłem-A) do działającego obiektu Thread. Bez istniejącego obiektu klasy zewnętrznej nie ma szansy na istniejącą metodę m1() i bez istniejącej metody m1() nie ma szansy na istnienie jej zmiennej lokalnej, ale jeśli obiekt klasy wewnętrznej używa zmiennej lokalnej metody m1(), to wszystko jest zrozumiałe .

Aby rozwiązać ten problem, musimy utworzyć kopię zmiennej lokalnej, a następnie skopiować ją do sterty za pomocą obiektu klasy Inner, co ja robi tylko dla zmiennej końcowej, ponieważ tak naprawdę nie są zmienne, są jak stałe (wszystko dzieje się w czasie kompilacji tylko nie w czasie wykonywania).