Pourquoi ne puis-je pas appeler une méthode en dehors d'une classe anonyme du même nom?
-
05-07-2019 - |
Question
Le code à la fin produit une erreur de compilation:
NotApplicable.java:7: run() in cannot be applied to (int)
run(42);
^
1 error
La question est pourquoi? Pourquoi javac pense-t-il que j'appelle run () et ne trouve-t-il pas run (int bar)? Il a correctement appelé foo (int bar). Pourquoi dois-je utiliser NotApplicable.this.run (42) ;? Est-ce un 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) {
}
}
La solution
L'explication du comportement de votre exemple de code est que this
est défini comme étant la classe dans laquelle vous êtes actuellement "most". à l'intérieur de. Dans ce cas, vous êtes "le plus". à l'intérieur de la classe interne anonyme qui sous-classe runnable et aucune méthode ne correspond à run (int)
. Pour élargir votre recherche, spécifiez le ce
que vous souhaitez utiliser en indiquant NotApplicable.this.run (42)
.
Le JVM évaluera comme suit:
this
- > instance en cours d'exécution de Runnable
avec la méthode run ()
NotApplicable.this
- > instance en cours d'exécution de NotApplicable
avec la méthode run (int)
Le compilateur recherchera dans l'arbre de nidification la première méthode qui correspond au nom de la méthode. & # 8211; Merci à DJClayworth pour cette clarification
La classe interne anonyme n'est pas une sous-classe de la classe externe. En raison de cette relation, la classe interne et la classe externe doivent pouvoir utiliser une méthode avec exactement la même signature et le bloc de code le plus interne doit pouvoir identifier la méthode à exécuter.
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" );
}
}
Autres conseils
Pour autant que je m'en souvienne, les règles de sélection d'une méthode à exécuter entre des classes imbriquées sont approximativement les mêmes que les règles de sélection d'une méthode dans un arbre d'héritage. Cela signifie que ce que nous obtenons ici n'est pas une surcharge, mais une dissimulation. La différence entre eux est cruciale pour comprendre les méthodes en héritage.
Si votre Runnable était déclaré en tant que sous-classe, la méthode run () masquerait la méthode run (int) dans le parent. Tout appel à exécuter (...) essaierait d'exécuter celui-ci sur Runnable, mais échouerait s'il ne correspondait pas aux signatures. Puisque foo n’est pas déclaré dans l’enfant, celui du parent est appelé.
Le même principe se passe ici. Recherchez des références à " méthode masquant " et il devrait être clair.
Cela est dû au fait que run
est re-déclaré lorsque vous entrez la portée new Runnable () {}
. Toutes les liaisons précédentes à exécuter deviennent inaccessibles. C'est comme si vous faisiez ceci:
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();
}
}
Le compilateur ne cherchera pas quelque chose qui correspond au type de x
tout le long de la pile de la portée, il s'arrêtera juste lorsqu'il trouvera les premières références et verra que les types sont incompatibles. .
REMARQUE: ce n'est pas comme si elle ne pouvait pas faire ceci ... c'est simplement que, pour préserver votre santé mentale, il a été décidé que cela ne devrait pas .