Por que não posso chamar um fora método de uma classe anônima de mesmo nome
-
05-07-2019 - |
Pergunta
O código no final produz um erro de compilação:
NotApplicable.java:7: run() in cannot be applied to (int)
run(42);
^
1 error
A questão é por quê? Por que javac acha que eu estou chamando run (), e não encontra run (int bar)? -Lo corretamente chamado foo (int bar). Por que eu tenho que usar NotApplicable.this.run (42) ;? É um bug?
public class NotApplicable {
public NotApplicable() {
new Runnable() {
public void run() {
foo(42);
run(42);
// uncomment below to fix
//NotApplicable.this.run(42);
}
};
}
private void run(int bar) {
}
public void foo(int bar) {
}
}
Solução
A explicação para o comportamento do seu exemplo de código é que this
é definido como sendo a classe que você está atualmente dentro "a maioria" de. Neste caso, você é "mais" dentro da classe interna anônima que subclasses executável e não existe um método que combina run(int)
. Para alargar a sua pesquisa que você especificar quais this
que deseja utilizar, afirmando NotApplicable.this.run(42)
.
A JVM irá avaliar o seguinte:
this
-> atualmente em execução instância de Runnable
com run()
método
NotApplicable.this
-> atualmente em execução instância de NotApplicable
com run(int)
método
O compilador irá procurar a árvore de nidificação para o primeiro método que corresponda ao nome do método. -Obrigado para DJClayworth para este esclarecimento
A classe interna anônima não é uma subclasse da classe externa. Devido a esta relação, tanto a classe interna e da classe externa deve ser capaz de ter um método com exatamente a mesma assinatura e o bloco de código mais interna deve ser capaz de identificar qual o método que pretende executar.
public class Outer{
public Outer() {
new Runnable() {
public void printit() {
System.out.println( "Anonymous Inner" );
}
public void run() {
printit(); // prints "Anonymous Inner"
this.printit(); //prints "Anonymous Inner"
// would not be possible to execute next line without this behavior
Outer.this.printit(); //prints "Outer"
}
};
}
public void printit() {
System.out.println( "Outer" );
}
}
Outras dicas
Tanto quanto eu recordar as regras para a seleção de um método para executar entre classes aninhadas são aproximadamente o mesmo que as regras para a seleção de um método em uma árvore de herança. Isso significa que o que estamos recebendo aqui não é sobrecarga, ele está escondendo. A diferença entre estes é crucial para métodos de compreensão em herança.
Se o seu Runnable foi declarada como uma subclasse, então o método run () iria esconder o (int) método no pai prazo. Qualquer chamada para execução (...) iria tentar executar a um em Runnable, mas seria um fracasso se não poderia coincidir com assinaturas. Desde foo não é declarado na criança, em seguida, a da mãe é chamado.
O mesmo princípio está acontecendo aqui. Olhe-se referências a "esconder método" e deve ficar claro.
Isto porque run
está sendo re-declarada quando você entra no âmbito new Runnable() {}
. Todas as ligações anteriores para executar tornam-se inacessíveis. É como se você estivesse fazendo isso:
import java.util.*;
public class tmp
{
private int x = 20;
public static class Inner
{
private List x = new ArrayList();
public void func()
{
System.out.println(x + 10);
}
}
public static void main(String[] args)
{
(new Inner()).func();
}
}
O compilador não vai olhar para algo que corresponde ao tipo de x
todo o caminho até a pilha de escopo, ele só vai parar quando encontrar as primeiras referências e vê que os tipos são incompatíveis.
NOTA: Não é como se não fazer isso ... é só que, para preservar a sua própria sanidade, ele foi decidido que não deveria .