Отредактировано: мне нужно изменить значения нескольких переменных, так как они запускаются несколько раз через таймер. Мне нужно постоянно обновлять значения с каждой итерацией через таймер. Я не могу установить значения final, поскольку это не позволит мне обновить значения, однако я получаю ошибку, которую я описал в начальном вопросе ниже:
Я ранее написал, что ниже:
Я получаю сообщение об ошибке "не может ссылаться на не финальную переменную внутри внутреннего класса, определенного в другом методе".
Это происходит для двойной цены, а цена называется ценой объекта. Ты знаешь, почему у меня такая проблема. Я не понимаю, почему мне нужно иметь окончательную декларацию. Также, если вы видите, что я пытаюсь сделать, что мне нужно сделать, чтобы обойти эту проблему.
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);
}
источник
Ответы:
Java не поддерживает истинные замыкания , хотя использование анонимного класса, как вы используете здесь (
new TimerTask() { ... }
), выглядит как своего рода замыкание.изменить - см. комментарии ниже - следующее не является правильным объяснением, как указывает KeeperOfTheSoul.
Вот почему это не работает:
Переменные
lastPrice
и цена являются локальными переменными в методе main (). Объект, который вы создаете с помощью анонимного класса, может длиться до тех пор, покаmain()
метод не вернется.Когда
main()
метод вернется, локальные переменные (такие какlastPrice
иprice
) будут удалены из стека, поэтому они больше не будут существовать послеmain()
возврата.Но анонимный объект класса ссылается на эти переменные. Все пошло бы ужасно неправильно, если бы объект анонимного класса попытался получить доступ к переменным после того, как они были очищены.
Делая
lastPrice
иprice
final
, они уже не переменные, а константы. Затем компилятор может просто заменить использованиеlastPrice
иprice
в анонимном классе значениями констант (конечно, во время компиляции), и у вас больше не будет проблем с доступом к несуществующим переменным.Другие языки программирования, которые поддерживают замыкания, делают это путем специальной обработки этих переменных - следя за тем, чтобы они не уничтожались при завершении метода, чтобы замыкание все еще могло обращаться к переменным.
@ Анкур: Вы могли бы сделать это:
источник
Чтобы избежать странных побочных эффектов с замыканиями в java-переменных, на которые ссылается анонимный делегат, они должны быть помечены как окончательные, поэтому для ссылки
lastPrice
и цены в задаче таймера их необходимо пометить как окончательные.Это, очевидно, не будет работать для вас, потому что вы хотите изменить их, в этом случае вы должны смотреть на их инкапсуляцию в классе.
Теперь просто создайте новый Foo как final и вызовите .tick из таймера.
источник
Вы можете получить доступ только к конечным переменным из содержащего класса при использовании анонимного класса. Поэтому вам нужно объявить используемые переменные final (что не подходит для вас, так как вы меняете lastPrice и цену ), или не использовать анонимный класс.
Таким образом, вы можете создать реальный внутренний класс, в котором вы можете передавать переменные и использовать их обычным способом.
или:
Существует быстрый (и, на мой взгляд, некрасивый) хак для вашей переменной lastPrice и цены, который должен объявить это так
и в своем анонимном классе вы можете установить значение, как это
источник
Хорошие объяснения того, почему вы не можете делать то, что вы пытаетесь сделать, уже предоставлены. В качестве решения, возможно, рассмотрим:
Похоже, что вы могли бы сделать лучший дизайн, чем это, но идея в том, что вы можете сгруппировать обновленные переменные внутри ссылки на класс, который не изменяется.
источник
С анонимными классами вы фактически объявляете «безымянный» вложенный класс. Для вложенных классов компилятор генерирует новый автономный открытый класс с конструктором, который будет принимать все переменные, которые он использует в качестве аргументов (для «именованных» вложенных классов это всегда экземпляр исходного / включающего класса). Это сделано потому, что среда выполнения не имеет понятия о вложенных классах, поэтому необходимо выполнить (автоматическое) преобразование из вложенного в автономный класс.
Возьмите этот код для примера:
Это не сработает, потому что это то, что компилятор делает изнутри:
Исходный анонимный класс заменяется некоторым автономным классом, который генерирует компилятор (код не точный, но должен дать вам хорошее представление):
Как вы можете видеть, автономный класс содержит ссылку на общий объект, помните, что все в java передаются по значению, поэтому даже если переменная ссылки 'shared' в EnclosingClass изменяется, экземпляр, на который он указывает, не изменяется и все другие ссылочные переменные, указывающие на него (как, например, в анонимном классе: с вложением $ 1), не будут знать об этом. Это основная причина, по которой компилятор заставляет вас объявлять эти «общие» переменные как окончательные, чтобы этот тип поведения не превращался в уже запущенный код.
Вот что происходит, когда вы используете переменную экземпляра внутри анонимного класса (это то, что вы должны сделать, чтобы решить свою проблему, переместить свою логику в метод «экземпляра» или конструктор класса):
Это прекрасно скомпилируется, потому что компилятор изменит код, так что новый сгенерированный класс Enclosing $ 1 будет содержать ссылку на экземпляр EnclosingClass, где он был создан (это только представление, но оно должно помочь вам):
Подобным образом, когда ссылочная переменная 'shared' в EnclosingClass переназначается, и это происходит до вызова Thread # run (), вы увидите, что "другой привет" напечатан дважды, потому что теперь вложенная переменная EnclosingClass $ 1 # будет сохранять ссылку объекту класса, в котором он был объявлен, поэтому изменения любого атрибута этого объекта будут видны экземплярам EnclosingClass $ 1.
Для получения дополнительной информации по этому вопросу, вы можете увидеть это отличное сообщение в блоге (не написано мной): http://kevinboone.net/java_inner.html
источник
Когда я сталкиваюсь с этой проблемой, я просто передаю объекты во внутренний класс через конструктор. Если мне нужно передать примитивы или неизменяемые объекты (как в этом случае), нужен класс-обертка.
Изменить: На самом деле я вообще не использую анонимный класс, но правильный подкласс:
источник
Вы не можете ссылаться на не финальные переменные, потому что так сказано в спецификации языка Java. Начиная с 8.1.3:
«Любая локальная переменная, формальный параметр метода или параметр обработчика исключений, используемые, но не объявленные во внутреннем классе, должны быть объявлены окончательными». Весь абзац.
Я вижу только часть вашего кода - по моему мнению, планирование изменения локальных переменных - странная идея. Локальные переменные перестают существовать, когда вы выходите из функции. Может быть, статические поля класса были бы лучше?
источник
Я просто написал что - то ручкой что - то вдоль намерения авторов . Я обнаружил, что лучше всего позволить конструктору взять все объекты, а затем в вашем реализованном методе использовать эти объекты конструктора.
Тем не менее, если вы пишете универсальный интерфейсный класс, то вам нужно передать объект, а точнее список объектов. Это может быть сделано Object [] или, что еще лучше, Object ... потому что его проще вызвать.
Смотрите мой пример чуть ниже.
Пожалуйста, смотрите этот пост о Java-замыканиях, которые поддерживают это из коробки: http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html
Версия 1 поддерживает прохождение нефинальных замыканий с автокастингом:
https://github.com/MSeifeddo/Closure-implementation-for-Java-5-6-and-7/blob/master/org/mo/closure/v1/ Closure.java
источник
Если вы хотите изменить значение в вызове метода в анонимном классе, это значение на самом деле является
Future
. Итак, если вы используете Гуава, вы можете написатьисточник
Одно решение, которое я заметил, не упомянуто (если я не пропустил его, если я действительно исправил меня), это использование переменной класса. Побежал в этот вопрос пытается запустить новый поток внутри метода:
new Thread(){ Do Something }
.Звонок
doSomething()
со следующего будет работать. Вам не обязательно объявлять егоfinal
, просто нужно изменить область действия переменной, чтобы она не собиралась до внутреннего класса. Это если, конечно, ваш процесс не огромен и изменение области действия может привести к конфликту. Я не хотел делать мою переменную финальной, поскольку она никоим образом не была финальной / постоянной.источник
Если переменная должна быть конечной, не может быть, тогда вы можете присвоить значение переменной другой переменной и сделать THAT окончательным, чтобы вы могли использовать его вместо этого.
источник
используйте ClassName.this.variableName для ссылки на не финальную переменную
источник
Вы можете просто объявить переменную вне внешнего класса. После этого вы сможете редактировать переменную из внутреннего класса. Иногда я сталкиваюсь с подобными проблемами при кодировании в Android, поэтому я объявляю переменную глобальной, и она работает для меня.
источник
Вы можете сделать
lastPrice
,priceObject
иprice
поле анонимного внутреннего класса?источник
Основная проблема заключается в том, может ли переменная внутри экземпляра анонимного класса быть разрешена во время выполнения. Не обязательно делать переменную окончательной, если гарантируется, что переменная находится внутри области выполнения. Например, посмотрите две переменные _statusMessage и _statusTextView внутри метода updateStatus ().
источник
то, что сработало для меня, это просто определить переменную вне этой вашей функции.
Непосредственно перед объявлением основной функции т.е.
источник
Объявите переменную как статическую и ссылайтесь на нее в необходимом методе, используя className.variable
источник
Non-static parameter cannot be referenced from a static context
Просто другое объяснение. Рассмотрим этот пример ниже
Здесь вывод будет
m1 завершен
Нить т работает
Нить т работает
Нить т работает
................
Теперь метод m1 () завершается, и мы присваиваем ссылочную переменную o пустому значению. Теперь Outer Class Object подходит для GC, но Inner Class Object все еще существует, у которого есть (Has-A) связь с запущенным объектом Thread. Без существующего объекта класса Outer нет шансов на существующий метод m1 (), а без существующего метода m1 () нет шансов на существование его локальной переменной, но если Inner Class Object использует локальную переменную метода m1 (), то все говорит само за себя ,
Чтобы решить эту проблему, мы должны создать копию локальной переменной, а затем скопировать ее затем в кучу с объектом класса Inner, что java делает только для конечной переменной, потому что они на самом деле не являются переменными, они как константы (все происходит только во время компиляции не во время выполнения).
источник
Чтобы решить вышеуказанную проблему, разные языки принимают разные решения.
для Java решение - то, что мы видим в этой статье.
для C # решение - разрешить побочные эффекты, и захват по ссылке - единственный вариант.
для C ++ 11 решение состоит в том, чтобы позволить программисту принять решение. Они могут выбрать захват по значению или по ссылке. При захвате по значению никаких побочных эффектов не произойдет, поскольку указанная переменная на самом деле отличается. При захвате по ссылке могут возникнуть побочные эффекты, но программист должен это понимать.
источник
Потому что сбивает с толку, если переменная не является окончательной, так как изменения в ней не будут приняты в анонимном классе.
Просто сделайте переменные 'price' и 'lastPrice' final.
-- Редактировать
Упс, и вам также нужно не назначать им, очевидно, в вашей функции. Вам понадобятся новые локальные переменные. В любом случае, я подозреваю, что кто-то уже дал вам лучший ответ.
источник