-
a
ne peuvent être finalisés qu'ici. Pourquoi ? Comment puis-je réaffectera
suronClick()
sans le garder comme membre privé ?private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; } }); }
-
Comment puis-je renvoyer le
5*a
quand ça a cliqué ? Je veux dire,private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; return b; // but return type is void } }); }
Réponses
Trop de publicités?Comme indiqué dans les commentaires, une grande partie de ces éléments ne sont plus pertinents dans Java 8, où final
peut être implicite.
C'est essentiellement dû à la façon dont Java gère fermetures .
Lorsque vous créez une instance d'une classe interne anonyme, toutes les variables qui sont utilisées dans cette classe ont leur valeurs copié via le constructeur autogénéré. Cela évite au compilateur de devoir autogénérer divers types supplémentaires pour contenir l'état logique des "variables locales", comme le fait par exemple le compilateur C#... (Lorsque C# capture une variable dans une fonction anonyme, il capture réellement la variable - la fermeture peut mettre à jour la variable d'une manière qui est vue par le corps principal de la méthode, et vice versa).
Comme la valeur a été copiée dans l'instance de la classe interne anonyme, il serait étrange que la variable puisse être modifiée par le reste de la méthode - vous pourriez avoir un code qui semble travailler avec une variable périmée (parce que c'est effectivement ce que la méthode serait vous travailleriez avec une copie prise à un moment différent). De même, si vous pouviez effectuer des modifications dans la classe interne anonyme, les développeurs pourraient s'attendre à ce que ces modifications soient visibles dans le corps de la méthode englobante.
Le fait de rendre la variable finale élimine toutes ces possibilités : comme la valeur ne peut pas être modifiée du tout, vous n'avez pas à vous soucier de la visibilité de ces modifications. La seule façon de permettre à la méthode et à la classe interne anonyme de voir les changements de l'autre est d'utiliser un type mutable d'une certaine description. Il peut s'agir de la classe englobante elle-même, d'un tableau, d'un type enveloppant mutable... n'importe quoi de ce genre. En fait, c'est un peu comme la communication entre une méthode et une autre : les changements apportés à la classe interne anonyme sont visibles. paramètres d'une méthode ne sont pas vues par son appelant, mais les modifications apportées aux objets visé par les paramètres sont vus.
Si vous êtes intéressé par une comparaison plus détaillée entre les fermetures de Java et de C#, j'ai un article sur le sujet. article qui l'approfondit. Je voulais me concentrer sur le côté Java dans cette réponse :)
Il existe une astuce qui permet aux classes anonymes de mettre à jour les données dans la portée externe.
private void f(Button b, final int a) {
final int[] res = new int[1];
b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
res[0] = a*5;
}
});
// But at this point handler is most likely not executed yet!
// How should we now res[0] is ready?
}
Cependant, cette astuce n'est pas très bonne en raison des problèmes de synchronisation. Si le gestionnaire est invoqué plus tard, vous devez 1) synchroniser l'accès à res si le gestionnaire a été invoqué depuis un autre thread 2) avoir une sorte de drapeau ou d'indication que res a été mis à jour.
Cette astuce fonctionne bien, cependant, si la classe anonyme est invoquée dans le même thread immédiatement. Comme :
...
final int[] res = new int[1];
Runnable r = new Runnable() { public void run() { res[0] = 123; } };
r.run();
System.out.println(res[0]);
...
Une classe anonyme est une classe intérieure et la règle stricte s'applique à classes intérieures (JLS 8.1.3) :
Toute variable locale, tout paramètre de méthode formelle ou tout paramètre de gestionnaire d'exception utilisé mais non déclaré dans une classe interne. doit être déclaré final . Toute variable locale, utilisée mais non déclarée dans une classe interne. doit être définitivement affecté avant le corps de la classe interne .
Je n'ai pas encore trouvé de raison ou d'explication dans le jls ou le jvms, mais nous savons que le compilateur crée un fichier de classe séparé pour chaque classe interne et qu'il doit s'assurer que les méthodes déclarées dans ce fichier de classe (au niveau du byte code) ont au moins accès aux valeurs des variables locales.
( Jon a la réponse complète - Je garde celui-ci non supprimé car on pourrait être intéressé par la règle JLS)
Vous pouvez créer une variable de niveau classe pour obtenir la valeur retournée. Je veux dire
class A {
int k=0;
private void f(IButton b, int a){
b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
// TODO Auto-generated method stub
k = a*5;
}
});
}
maintenant vous pouvez obtenir la valeur de K et l'utiliser où vous voulez.
La réponse à votre question est :
Une instance locale de classe interne est liée à la classe Main et peut accéder aux variables locales finales de la méthode qu'elle contient. Lorsque l'instance utilise une variable locale finale de la méthode qui la contient, la variable conserve la valeur qu'elle avait au moment de la création de l'instance, même si la variable est sortie de son champ d'application (il s'agit en fait de la version grossière et limitée des fermetures de Java).
Comme une classe interne locale n'est pas membre d'une classe ou d'un paquetage, elle n'est pas déclarée avec un niveau d'accès. (Il est clair, cependant, que ses propres membres ont des niveaux d'accès comme dans une classe normale).
Eh bien, en Java, une variable peut être finale non seulement en tant que paramètre, mais aussi en tant que champ de niveau classe, comme
public class Test
{
public final int a = 3;
ou comme une variable locale, comme
public static void main(String[] args)
{
final int a = 3;
Si vous voulez accéder à une variable et la modifier à partir d'une classe anonyme, vous pouvez faire de cette variable un objet de type au niveau de la classe dans le enfermant classe.
public class Test
{
public int a;
public void doSomething()
{
Runnable runnable =
new Runnable()
{
public void run()
{
System.out.println(a);
a = a+1;
}
};
}
}
Vous ne pouvez pas avoir une variable comme finale et lui donner une nouvelle valeur. final
signifie exactement cela : la valeur est immuable et définitive.
Et comme c'est définitif, Java peut sans risque copie à des classes anonymes locales. Tu ne vas pas obtenir référence à l'int (d'autant plus que vous ne pouvez pas avoir de références à des primitives comme int en Java, seulement des références à Objets ).
Elle copie simplement la valeur de a dans un int implicite appelé a dans votre classe anonyme.
- Réponses précédentes
- Plus de réponses