Edytowane: Muszę zmienić wartości kilku zmiennych, ponieważ działają one kilkakrotnie przez licznik czasu. Muszę aktualizować wartości przy każdej iteracji za pomocą timera. Nie mogę ustawić wartości na końcowe, ponieważ nie pozwoli mi to zaktualizować wartości, jednak pojawia się błąd, który opisuję w początkowym pytaniu poniżej:
Wcześniej napisałem, co jest poniżej:
Pojawia się błąd „nie można odwoływać się do nieokreślonej zmiennej w wewnętrznej klasie zdefiniowanej w innej metodzie”.
Dzieje się tak w przypadku podwójnej ceny o nazwie Cena i ceny o nazwie cena Obiekt. Czy wiesz, dlaczego mam ten problem? Nie rozumiem, dlaczego potrzebuję ostatecznej deklaracji. Również jeśli widzisz, 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);
}
źródło
Odpowiedzi:
Java nie obsługuje prawdziwych zamknięć , nawet jeśli użycie anonimowej klasy, takiej jak używasz tutaj (
new TimerTask() { ... }
), wygląda jak zamknięcie.edycja - zobacz poniższe komentarze - poniższe informacje nie są poprawnym wyjaśnieniem, jak zauważa KeeperOfTheSoul.
Oto dlaczego to nie działa:
Zmienne
lastPrice
i cena są zmiennymi lokalnymi w metodzie main (). Obiekt utworzony za pomocą klasy anonimowej może trwać do momentumain()
powrotu metody.Kiedy
main()
metoda zwróci, zmienne lokalne (takie jaklastPrice
iprice
) zostaną usunięte ze stosu, więc nie będą już istnieć pomain()
powrocie.Ale anonimowy obiekt klasy odwołuje się do tych zmiennych. Sprawy potoczyłyby się bardzo źle, gdyby obiekt klasy anonimowej próbował uzyskać dostęp do zmiennych po ich oczyszczeniu.
Tworząc
lastPrice
iprice
final
, nie są już tak naprawdę zmiennymi, ale stałymi. Kompilator może wtedy po prostu zastąpić użycielastPrice
iwprice
klasie anonimowej wartościami stałych (oczywiście w czasie 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 przez specjalne traktowanie tych zmiennych - upewniając się, że nie zostaną zniszczone po zakończeniu metody, aby zamknięcie nadal mogło uzyskać dostęp do zmiennych.
@Ankur: Możesz to zrobić:
źródło
Aby uniknąć dziwnych efektów ubocznych związanych z zamykaniem zmiennych java, do których odwołuje się anonimowy delegat, należy oznaczyć je jako ostateczne, więc aby odwoływać się
lastPrice
i wyceniać w ramach zadania czasowego, muszą być oznaczone jako ostateczne.To oczywiście nie zadziała, ponieważ chcesz je zmienić, w takim przypadku powinieneś spojrzeć na kapsułkowanie ich w klasie.
teraz po prostu utwórz nowy Foo jako ostateczny i zadzwoń .tick z timera.
źródło
Dostęp do zmiennych końcowych można uzyskać tylko z klasy zawierającej, gdy używana jest klasa anonimowa. Dlatego musisz zadeklarować, że zmienne są używane jako ostateczne (co nie jest dla ciebie opcją, ponieważ zmieniasz ostatnią cenę i cenę ) lub nie korzystasz z anonimowej klasy.
Zatem twoimi opcjami jest utworzenie rzeczywistej klasy wewnętrznej, w której możesz przekazać zmienne i używać ich w normalny sposób
lub:
Istnieje szybki (i moim zdaniem brzydki) hack dla ostatniej zmiennej ceny i ceny , która ma ją tak zadeklarować
a w swojej anonimowej klasie możesz ustawić taką wartość
źródło
Dobre wyjaśnienia, dlaczego nie możesz zrobić tego, co próbujesz, już dostarczone. Jako rozwiązanie warto rozważyć:
Wygląda na to, że prawdopodobnie mógłbyś zrobić lepszy projekt, ale pomysł polega na tym, że możesz grupować zaktualizowane zmienne wewnątrz odwołania do klasy, które się nie zmienia.
źródło
Dzięki anonimowym klasom faktycznie deklarujesz „bezimienną” klasę zagnieżdżoną. W przypadku klas zagnieżdżonych kompilator generuje nową autonomiczną klasę publiczną z konstruktorem, który weźmie wszystkie zmienne, których używa jako argumenty (w przypadku klas nazwanych zagnieżdżonych jest to zawsze instancja klasy oryginalnej / zamykającej). Dzieje się tak, ponieważ środowisko wykonawcze nie ma pojęcia o zagnieżdżonych klasach, dlatego konieczna jest (automatyczna) konwersja z zagnieżdżonej do samodzielnej klasy.
Weźmy na przykład ten kod:
To nie zadziała, ponieważ to właśnie robi kompilator pod maską:
Oryginalną anonimową klasę zastępuje pewna samodzielna klasa generowana przez kompilator (kod nie jest dokładny, ale powinien dać ci dobry pomysł):
Jak widać, samodzielna klasa zawiera odniesienie do obiektu współdzielonego, pamiętaj, że wszystko w java jest przekazywane przez wartość, więc nawet jeśli zmienna odniesienia „shared” w EnclosingClass zostanie zmieniona, instancja, na którą wskazuje, nie jest modyfikowana , i wszystkie inne zmienne odniesienia do niego wskazujące (jak ta w klasie anonimowej: Załączanie 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 rodzaju zachowanie nie przekształciło się w już działający kod.
Oto, co dzieje się, gdy używasz zmiennej instancji w anonimowej klasie (to powinieneś zrobić, aby rozwiązać problem, przenieść logikę do metody „instancji” lub konstruktora klasy):
To kompiluje się dobrze, ponieważ kompilator zmodyfikuje kod, tak aby nowa generowana klasa Enclosing $ 1 zawierała odwołanie do instancji EnclosingClass, w której została utworzona instancja (jest to tylko reprezentacja, ale powinna zacząć działać):
W ten sposób, gdy zmienna referencyjna „współdzielona” w EnclosingClass zostanie ponownie przypisana, a dzieje się to przed wywołaniem wątku # run (), zobaczysz dwa razy wydrukowane „other hello”, ponieważ teraz zmienna otaczająca EnclosingClass $ 1 zachowa referencję do obiektu klasy, w której został zadeklarowany, więc zmiany dowolnego atrybutu tego obiektu będą widoczne dla instancji klasy EnclosingClass $ 1.
Aby uzyskać więcej informacji na ten temat, możesz zobaczyć ten znakomity post na blogu (nie napisany przeze mnie): http://kevinboone.net/java_inner.html
źródło
Kiedy natknę się na ten problem, po prostu przekazuję obiekty do klasy wewnętrznej przez konstruktora. Jeśli muszę przekazać prymitywy lub niezmienne obiekty (jak w tym przypadku), potrzebna jest klasa opakowania.
Edycja: Właściwie wcale nie używam anonimowej klasy, ale odpowiednią podklasę:
źródło
Nie można odwoływać się do zmiennych nieokreślonych, ponieważ tak określa specyfikacja języka Java. Od 8.1.3:
„Każda zmienna lokalna, parametr metody formalnej lub parametr procedury obsługi wyjątków użyty, ale nie zadeklarowany w klasie wewnętrznej, musi zostać zadeklarowany jako ostateczny.” Cały akapit.
Widzę tylko część twojego kodu - według mnie planowanie modyfikacji zmiennych lokalnych jest dziwnym pomysłem. Zmienne lokalne przestają istnieć po opuszczeniu funkcji. Może lepsze byłyby pola statyczne klasy?
źródło
Właśnie napisałem coś, aby poradzić sobie z czymś zgodnie z intencją autorów . Znalazłem najlepszą rzeczą do zrobienia było pozwolenie, aby konstruktor wziął wszystkie obiekty, a następnie w zaimplementowanej metodzie wykorzystał te obiekty konstruktora.
Jeśli jednak piszesz ogólną klasę interfejsu, musisz przekazać obiekt lub, lepiej, listę obiektów. Można to zrobić przez Object [] lub jeszcze lepiej, Object ..., ponieważ łatwiej jest wywoływać.
Zobacz mój przykładowy kawałek poniżej.
Zapoznaj się z tym postem na temat zamknięć Java, które obsługują to od razu: http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html
Wersja 1 obsługuje przekazywanie nie-końcowych zamknięć z autocastingiem:
https://github.com/MSeifeddo/Closure-implementation-for-Java-5-6-and-7/blob/master/org/mo/closure/v1/ Closure.java
źródło
Jeśli chcesz zmienić wartość w wywołaniu metody w klasie anonimowej, ta „wartość” jest w rzeczywistości
Future
. Więc jeśli używasz Guava, możesz pisaćźródło
Jedno z rozwiązań, które zauważyłem, nie jest wspomniane (chyba że je przegapiłem, jeśli to zrobię, proszę mnie poprawić), jest użycie zmiennej klasy. Wpadł na ten problem, próbujący uruchomić nowy wątek w metodzie:
new Thread(){ Do Something }
.Dzwoń
doSomething()
z następujących elementów będzie działać. Nie musisz go deklarowaćfinal
, wystarczy zmienić zakres zmiennej, aby nie była ona gromadzona przed klasą wewnętrzną. Dzieje się tak, chyba że oczywiście Twój proces jest ogromny, a zmiana zakresu może spowodować konflikt. Nie chciałem, aby moja zmienna była ostateczna, ponieważ w żaden sposób nie była ostateczna / stała.źródło
Jeśli zmienna musi być ostateczna, nie może być, możesz przypisać wartość zmiennej do innej zmiennej i sprawić, że będzie ona ostateczna, abyś mógł jej użyć.
źródło
użyj ClassName.this.variableName, aby odwołać się do nie-końcowej zmiennej
źródło
możesz po prostu zadeklarować zmienną poza klasą zewnętrzną. Następnie będziesz mógł edytować zmienną z klasy wewnętrznej. Czasami napotykam podobne problemy podczas kodowania w Androidzie, dlatego deklaruję zmienną jako globalną i działa ona dla mnie.
źródło
Można zrobić
lastPrice
,priceObject
iprice
pola anonimowej klasy wewnętrznej?źródło
Głównym problemem jest to, czy zmienną wewnątrz anonimowej instancji klasy można rozwiązać w czasie wykonywania. Nie jest konieczne, aby zmienna była ostateczna, o ile gwarantowane jest, że zmienna znajduje się w zakresie wykonawczym. Na przykład zobacz dwie zmienne _statusMessage i _statusTextView w metodzie updateStatus ().
źródło
To, co zadziałało, to po prostu zdefiniowanie zmiennej poza tą funkcją.
Tuż przed deklaracją funkcji głównej tj
źródło
Zadeklaruj zmienną jako statyczną i odwołaj się do niej w wymaganej metodzie za pomocą className.variable
źródło
Non-static parameter cannot be referenced from a static context
To kolejne wyjaśnienie. Rozważ ten przykład poniżej
Tutaj Wyjście będzie
m1 Ukończono
Wątek t działa
Wątek t działa
Wątek t działa
................
Teraz metoda m1 () kończy się i przypisujemy zmienną referencyjną o wartości null, teraz obiekt klasy zewnętrznej kwalifikuje się do GC, ale nadal istnieje obiekt klasy wewnętrznej, który ma (Has-A) związek z działającym obiektem wątku. Bez istniejącego obiektu klasy zewnętrznej nie ma szans na istnienie metody m1 (), a bez istniejącej metody m1 () nie ma szansy na istnienie jej zmiennej lokalnej, ale jeśli obiekt klasy wewnętrznej korzysta z zmiennej lokalnej metody m1 (), wszystko jest oczywiste .
Aby rozwiązać ten problem, musimy utworzyć kopię zmiennej lokalnej, a następnie skopiować ją na stos z obiektem klasy wewnętrznej. To, co robi Java, tylko dla zmiennej końcowej, ponieważ tak naprawdę nie są zmienne, są jak stałe (wszystko dzieje się tylko w czasie kompilacji nie w czasie wykonywania).
źródło
Aby rozwiązać powyższy problem, różne języki podejmują różne decyzje.
w przypadku Javy rozwiązanie jest takie, jakie widzimy w tym artykule.
w przypadku C # rozwiązaniem jest dopuszczenie efektów ubocznych i jedyną opcją jest przechwytywanie przez odniesienie.
w przypadku C ++ 11 rozwiązaniem jest umożliwienie programistom podjęcia decyzji. Mogą wybrać przechwytywanie według wartości lub referencji. W przypadku przechwytywania według wartości nie wystąpiłyby żadne skutki uboczne, ponieważ przywoływana zmienna jest w rzeczywistości inna. W przypadku przechwytywania przez odniesienie mogą wystąpić działania niepożądane, ale programista powinien to zrozumieć.
źródło
Ponieważ jest mylące, jeśli zmienna nie jest ostateczna, ponieważ zmiany w niej nie zostaną wykryte w klasie anonimowej.
Po prostu ustaw zmienne „cena” i „ostatnia cena” jako ostateczne.
-- Edytować
Ups, musisz też nie przypisywać do nich, oczywiście, w swojej funkcji. Będziesz potrzebował nowych zmiennych lokalnych. W każdym razie podejrzewam, że ktoś udzielił ci lepszej odpowiedzi.
źródło