Editado: preciso alterar os valores de várias variáveis, pois elas são executadas várias vezes através de um timer. Eu preciso continuar atualizando os valores a cada iteração através do timer. Não consigo definir os valores para final, pois isso me impedirá de atualizar os valores, mas estou recebendo o erro descrito na pergunta inicial abaixo:
Eu escrevi anteriormente o que está abaixo:
Estou recebendo o erro "não pode se referir a uma variável não final dentro de uma classe interna definida em um método diferente".
Isso está acontecendo para o preço chamado duplo e o preço chamado priceObject. Você sabe por que eu recebo esse problema. Não entendo por que preciso ter uma declaração final. Além disso, se você puder ver o que estou tentando fazer, o que preciso fazer para solucionar esse problema.
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);
}
fonte
Respostas:
Java não suporta fechamentos verdadeiros , embora o uso de uma classe anônima como a que você está usando aqui (
new TimerTask() { ... }
) pareça um tipo de fechamento.editar - Veja os comentários abaixo - o seguinte não é uma explicação correta, como aponta o KeeperOfTheSoul.
É por isso que não funciona:
As variáveis
lastPrice
e o preço são variáveis locais no método main (). O objeto que você cria com a classe anônima pode durar até depois que omain()
método retornar.Quando o
main()
método retornar, variáveis locais (comolastPrice
eprice
) serão limpas da pilha, para que não existam mais apósmain()
retornos.Mas o objeto de classe anônimo faz referência a essas variáveis. As coisas dariam terrivelmente errado se o objeto de classe anônimo tentasse acessar as variáveis depois que elas fossem limpas.
Ao fazer
lastPrice
eprice
final
, eles não são mais variáveis, mas constantes. O compilador pode apenas substituir o uso delastPrice
eprice
na classe anônima pelos valores das constantes (em tempo de compilação, é claro), e você não terá mais o problema de acessar variáveis inexistentes.Outras linguagens de programação que suportam fechamentos o fazem tratando especialmente essas variáveis - garantindo que elas não sejam destruídas quando o método terminar, para que o fechamento ainda possa acessar as variáveis.
@Ankur: Você poderia fazer isso:
fonte
Para evitar efeitos colaterais estranhos com o fechamento de variáveis java referenciadas por um delegado anônimo, deve ser marcado como final; portanto, para se referir
lastPrice
e preço dentro da tarefa de timer, eles precisam ser marcados como finais.Obviamente, isso não funcionará para você porque você deseja alterá-los; nesse caso, você deve encapsulá-los em uma classe.
Agora, basta criar um novo Foo como final e chamar .tick a partir do timer.
fonte
Você só pode acessar variáveis finais da classe que contém quando usar uma classe anônima. Portanto, você precisa declarar as variáveis que estão sendo usadas como finais (o que não é uma opção para você, pois você está alterando lastPrice e price ) ou não usa uma classe anônima.
Portanto, suas opções são criar uma classe interna real, na qual você pode passar as variáveis e usá-las normalmente
ou:
Existe um hack rápido (e na minha opinião feio) para sua variável lastPrice e price , que é declará-la assim
e na sua classe anônima você pode definir o valor como este
fonte
Boas explicações sobre por que você não pode fazer o que está tentando fazer já foram fornecidas. Como solução, talvez considere:
Parece que provavelmente você poderia fazer um design melhor do que isso, mas a idéia é que você poderia agrupar as variáveis atualizadas dentro de uma referência de classe que não muda.
fonte
Com classes anônimas, você está realmente declarando uma classe aninhada "sem nome". Para classes aninhadas, o compilador gera uma nova classe pública independente com um construtor que aceita todas as variáveis usadas como argumentos (para classes aninhadas "nomeadas", essa é sempre uma instância da classe original / anexa). Isso é feito porque o ambiente de tempo de execução não tem noção de classes aninhadas, portanto, é necessário haver uma conversão (automática) de uma classe aninhada para uma classe independente.
Veja este código, por exemplo:
Isso não vai funcionar, porque é isso que o compilador faz sob o capô:
A classe anônima original é substituída por alguma classe autônoma que o compilador gera (o código não é exato, mas deve lhe dar uma boa idéia):
Como você pode ver, a classe autônoma mantém uma referência ao objeto compartilhado; lembre-se de que tudo em java é transmitido por valor; portanto, mesmo que a variável de referência 'compartilhada' em EnclosingClass seja alterada, a instância para a qual ela aponta não será modificada , e todas as outras variáveis de referência que apontam para ele (como a da classe anônima: Incluindo $ 1), não estarão cientes disso. Esse é o principal motivo pelo qual o compilador obriga a declarar essas variáveis 'compartilhadas' como finais, para que esse tipo de comportamento não seja inserido no código já em execução.
Agora, é isso que acontece quando você usa uma variável de instância dentro de uma classe anônima (é o que você deve fazer para resolver seu problema, mova sua lógica para um método de "instância" ou para um construtor de uma classe):
Isso compila bem, porque o compilador modifica o código, para que a nova classe gerada Enclosing $ 1 mantenha uma referência à instância de EnclosingClass em que foi instanciada (isso é apenas uma representação, mas você deve continuar):
Assim, quando a variável de referência 'compartilhada' em EnclosingClass for reatribuída, e isso acontecer antes da chamada para o Thread # run (), você verá "other hello" impresso duas vezes, porque agora a variável envolvente EnclosingClass $ 1 # manterá uma referência para o objeto da classe em que foi declarado, portanto, as alterações em qualquer atributo desse objeto serão visíveis para as instâncias de EnclosingClass $ 1.
Para mais informações sobre o assunto, você pode ver esta excelente postagem no blog (não escrita por mim): http://kevinboone.net/java_inner.html
fonte
Quando me deparei com esse problema, apenas passo os objetos para a classe interna através do construtor. Se eu precisar passar objetos primitivos ou imutáveis (como neste caso), será necessária uma classe de wrapper.
Edit: Na verdade, eu não uso uma classe anônima, mas uma subclasse adequada:
fonte
Você não pode se referir a variáveis não finais porque o Java Language Specification diz isso. De 8.1.3:
"Qualquer variável local, parâmetro formal de método ou parâmetro de manipulador de exceção usado, mas não declarado em uma classe interna, deve ser declarado final." Parágrafo inteiro.
Eu posso ver apenas parte do seu código - de acordo comigo, agendar modificações nas variáveis locais é uma ideia estranha. Variáveis locais deixam de existir quando você sai da função. Talvez campos estáticos de uma classe sejam melhores?
fonte
Acabei de escrever algo para lidar com algo de acordo com a intenção dos autores . Eu descobri que a melhor coisa a fazer era deixar o construtor pegar todos os objetos e, em seu método implementado, usar esses objetos construtores.
No entanto, se você estiver escrevendo uma classe de interface genérica, precisará passar um Objeto, ou melhor, uma lista de Objetos. Isso poderia ser feito por Object [] ou, melhor ainda, Object ... porque é mais fácil chamar.
Veja meu exemplo abaixo.
Consulte este post sobre fechamentos Java que suporta isso imediatamente : http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html
A versão 1 suporta a aprovação de encerramentos não finais com autocasting:
https://github.com/MSeifeddo/Closure-implementation-for-Java-5-6-and-7/blob/master/org/mo/closure/v1/ Closure.java
fonte
Se você deseja alterar um valor em uma chamada de método dentro de uma classe anônima, esse "valor" é na verdade a
Future
. Então, se você usa o Guava, pode escreverfonte
Uma solução que eu notei não é mencionada (a menos que eu errei, se por favor me corrija), é o uso de uma variável de classe. Corri para este problema de tentar executar um novo segmento dentro de um método:
new Thread(){ Do Something }
.Ligar a
doSomething()
partir do seguinte funcionará. Você não precisa necessariamente declará-lofinal
, basta alterar o escopo da variável para que ela não seja coletada antes da classe interna. A menos que, é claro, seu processo seja enorme e a alteração do escopo possa criar algum tipo de conflito. Eu não queria fazer minha variável final, pois não era de forma alguma uma final / constante.fonte
Se a variável requerida para ser final, não pode ser, você pode atribuir o valor da variável a outra variável e torná-lo final para que você possa usá-lo.
fonte
use ClassName.this.variableName para referenciar a variável não final
fonte
você pode simplesmente declarar a variável fora da classe externa. Depois disso, você poderá editar a variável de dentro da classe interna. Às vezes, enfrento problemas semelhantes ao codificar no Android, então declaro a variável como global e ela funciona para mim.
fonte
Você pode fazer
lastPrice
,priceObject
eprice
campos da classe interna anônima?fonte
A principal preocupação é se uma variável dentro da instância de classe anônima pode ser resolvida em tempo de execução. Não é necessário finalizar uma variável, desde que seja garantido que a variável esteja dentro do escopo de tempo de execução. Por exemplo, consulte as duas variáveis _statusMessage e _statusTextView dentro do método updateStatus ().
fonte
o que funcionou para mim é apenas definir a variável fora dessa função do seu.
Pouco antes da função principal declarar ie
fonte
Declare a variável como estática e faça referência a ela no método necessário usando className.variable
fonte
Non-static parameter cannot be referenced from a static context
Apenas mais uma explicação. Considere este exemplo abaixo
Aqui a saída será
m1 é concluído
Thread t running
Thread t running
Thread t running
................
Agora o método m1 () é concluído e atribuímos a variável de referência o a null, o Now Outer Class Object é elegível para GC, mas ainda existe o Inner Class Object que possui um relacionamento (Has-A) com o objeto Thread que está sendo executado. Sem o objeto de classe externa existente, não há chance do método m1 () existente e sem o método m1 () existente, não há chance de existir sua variável local, mas se o Inner Class Object usar a variável local do método m1 (), tudo será auto-explicativo. .
Para resolver isso, precisamos criar uma cópia da variável local e, em seguida, copiar no heap com o objeto da classe Inner, o que o java faz apenas para a variável final, porque na verdade não são variáveis, são como constantes (tudo acontece apenas no tempo de compilação não em tempo de execução).
fonte
Para resolver o problema acima, idiomas diferentes tomam decisões diferentes.
para Java, a solução é a que vemos neste artigo.
para C #, a solução é permitir efeitos colaterais e capturar por referência é a única opção.
para C ++ 11, a solução é permitir que o programador tome a decisão. Eles podem optar por capturar por valor ou por referência. Se capturar por valor, nenhum efeito colateral ocorreria porque a variável referenciada é realmente diferente. Se capturar por referência, efeitos colaterais podem ocorrer, mas o programador deve perceber.
fonte
Porque é confuso se a variável não for final, pois as alterações não serão identificadas na classe anônima.
Basta fazer as variáveis 'preço' e 'lastPrice' final.
- Editar
Opa, e você também não precisará atribuir a eles, obviamente, em sua função. Você precisará de novas variáveis locais. De qualquer forma, suspeito que alguém já tenha lhe dado uma resposta melhor.
fonte