web-dev-qa-db-fra.com

La dernière variable locale ne peut pas être affectée

J'ai un tableau de sièges et le tableau a deux chaînes (sélectionnées et vides). En cliquant avec la souris, je veux parcourir le tableau et trouver le siège sélectionné. Lorsque j'appuie sur le bouton, il dit:

La dernière variable locale seatno ne peut pas être affectée, car elle est définie dans un type englobant.

    JButton btnContinue = new JButton("Next");
    btnContinue.addMouseListener(new MouseAdapter() {
        public void mouseClicked(MouseEvent arg0) {

            for(int x=0;x<17;x++){
                if(anArray[x]=="selected"){

                    seatno = anArray[x];
                }
            }

            data page=new data(newfrom,newto,newtime,date2,seatno);
            page.setVisible(true);
            setVisible(false);
        }
    });
    btnContinue.setBounds(358, 227, 62, 23);
    contentPane.add(btnContinue);
36
user1326088

Le fait est que les variables locales de méthode du type englobant sont en fait copiées dans des instances de classes anonymes (c'est à cause de problèmes de trame d'activation, mais j'ai gagné n'entrerons pas dans les détails car cela n'est pas vraiment pertinent pour la question), c'est pourquoi ils doivent être finaux, car la variable dans l'instance de type imbriquée n'est plus la même.

Voici donc le premier exemple:

void foo() {
    int a = 3;
    new Runnable() {
        @Override
        public void run() {
            a += 3;
        }
    };
}

Cela ne se compile pas, car vous ne pouvez pas référencer une variable non finale dans une méthode de classe anonyme. Lorsque vous ajoutez un dernier modificateur à la déclaration de a, la valeur de a est copiée dans l'instance créée de la classe anonyme que vous avez définie. Toutefois, vous ne serez pas autorisé à modifier la valeur de a, car les modifications ne seront pas visibles par la méthode où a a été déclarée.

Cependant, les classes anonymes ne sont pas statiques, c'est-à-dire qu'elles ont une référence à l'instance englobante (sauf si la méthode où elles sont déclarées est statique) que vous pouvez utiliser pour modifier les variables de l'instance englobante:

int a = 3;

void foo() {
    new Runnable() {
        @Override
        public void run() {
            a += 3;
        }
    };
}

Cet exemple compile et augmenterait a de 3 chaque fois que la méthode run() de l'instance de la classe anonyme est appelée. (Dans cet exemple, il n'est jamais appelé, mais ce n'est qu'un exemple.)

Donc, pour résumer, vous devez convertir la variable seatno d'une variable locale de méthode en une variable d'instance du type englobant. Ou, s'il l'est encore, vous devez supprimer le dernier modificateur car les variables finales ne peuvent être attribuées qu'une seule fois.

Mise à jour: Dans Java 8, le concept de variables effectivement final est introduit (voir Spécification du langage Java ). Cependant, dans le premier exemple de cet article, la variable a est affectée plusieurs fois, ce qui l'empêche d'être effectivement définitive. Cela signifie que cet exemple ne compile toujours pas avec Java 8. (L'erreur de compilation est "La variable locale définie dans une étendue englobante doit être finale ou effectivement finale"))

94
Michael Schmeißer

Une variable finale ne peut pas changer sa valeur (elle est similaire à const de C/C++).

Vous voulez probablement en faire un champ dans une classe (sans le mot-clé final bien sûr), pas une variable locale à l'intérieur d'une fonction.

4
gulyan

Au lieu de définir une variable de membre de classe, vous pouvez également utiliser un int mutable pour obtenir la même chose.

void foo() {
    final MutableInt a = new MutableInt(3);
    new Runnable() {
        @Override
        public void run() {
           a.add(3);
        }
    };
}

Puisque MutableInt n'est pas de type primitif (donc transmis par référence) et peut être réaffecté, cela fonctionne.

4
Satyam Shekhar

J'ai récemment rencontré un problème similaire. Dans mon cas, il était plus facile de créer un tableau final (ou une collection) et d'ajouter une variable, que je voulais changer à l'intérieur d'une classe anonyme, dans ce tableau, comme ci-dessous.

   int a = 3;
   final int[] array = new int[1];
   array[0] = a;
   new Runnable() {
       @Override
       public void run() {
           array[0] += 3;
       }
   };
1
yurin

Sans connaître la déclaration de seatno, je suggère d'introduire une nouvelle variable dans la méthode mouseClicked() qui est not final et fait le même travail que seatno le fait actuellement, car la variable ne semble être utilisée qu'à l'intérieur de cette méthode.

Soit dit en passant: mettez en majuscule vos noms de classe (data devrait être Data). Sera beaucoup plus clair.

0
Yogu

Assurez-vous que votre variable n'a pas le modificateur final.

//final, can be set only when the object is created.
private final String seatno;

//no final modifier, the value can be set every time you "want"
private String seatno;

De plus, pour comparer des chaînes, vous devez utiliser equals:

if(anArray[x].equals("selected"))
0
Luiggi Mendoza