17

I'm trying to create an array of JLabels, all of them should go invisible when clicked. The problem comes when trying to set up the mouse listener through an inner class that needs access to the iteration variable of the loop used to declare the labels. Code is self-explanatory:

    for(int i=1; i<label.length; i++) {
       label[i] = new JLabel("label " + i);
       label[i].addMouseListener(new MouseAdapter() {
          public void mouseClicked(MouseEvent me) {
             label[i].setVisible(false);   // compilation error here
          }
       });
       cpane.add(label[i]);
    }

I thought that I could overcome this by the use of this or maybe super instead of the call of label[i] within the inner method but I haven't been able to figure it out.

The compilation error is: local variable i is accessed from within inner class; needs to be declared final`

I'm sure that the answer must be something really silly I haven't thought of or maybe I'm making some small mistake.

Any help would be appreciated

Oscar Wahltinez
  • 1,155
  • 3
  • 12
  • 24
  • The error is: `local variable i is accessed from within inner class; needs to be declared final` – Oscar Wahltinez Oct 10 '10 at 18:34
  • @omtinez: then perhaps you should declare the variable as `final`, no? (have a look at [this Q&A](http://stackoverflow.com/questions/3045130/constructors-in-inner-classes-implementing-interfaces), also) – Matt Ball Oct 10 '10 at 18:37
  • 1
    @Matt Ball, It isn't a good idea to have the iterator variable `final`. – Colin Hebert Oct 10 '10 at 18:37
  • Similar to http://stackoverflow.com/questions/14425826/variable-is-accessed-within-inner-class-needs-to-be-declared-final – Suragch Dec 05 '16 at 05:03

5 Answers5

27

Your local variable must be final to be accessed from the inner (and anonymous) class.

You can change your code for something like this :

for (int i = 1; i < label.length; i++) {
    final JLabel currentLabel =new JLabel("label " + i); 
    currentLabel.addMouseListener(new MouseAdapter() {
        public void mouseClicked(MouseEvent me) {
            currentLabel.setVisible(false);   // No more compilation error here
        }
    });
    label[i] = currentLabel;
}

From the JLS :

Any local variable, formal parameter, or exception parameter used but not declared in an inner class must be declared final.

Any local variable used but not declared in an inner class must be definitely assigned (§16) before the body of the inner class.


Resources :

Colin Hebert
  • 91,525
  • 15
  • 160
  • 151
  • it would be helpful to be able to use the iterator variable for other methods that I considered irrelevant to the question as well – Oscar Wahltinez Oct 10 '10 at 18:48
  • @Colin Hebert Your "resources" link seems linking other document than section 8.1.3 – trante May 10 '13 at 06:27
  • @trante, thanks it was referring to a previous JLS, hosted by sun. It's now up to date. – Colin Hebert May 10 '13 at 08:39
  • 1
    **Upd**: starting in Java SE 8, a local class can access local variables and parameters of the enclosing block that are final or effectively final. See [accessing members of an enclosing class](https://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html#accessing-members-of-an-enclosing-class) – Developer Marius Žilėnas Mar 18 '16 at 10:37
4

If you're having a problem accessing i, make another variable outside the scope of your inner-class (e.g. before label[i].addMouseListener(...)):

for(int i=1; i<label.length; i++) {
   label[i] = new JLabel("label " + i);

   final int localI = i;
   label[i].addMouseListener(new MouseAdapter() {
      public void mouseClicked(MouseEvent me) {
         label[localI].setVisible(false);
      }
   });
   cpane.add(label[i]);
}
2

you can also use getSource in your program. After this you can access your component with the help of typecasting. it will reduce extra lines of code, your code will look like this

for (int i = 1; i < label.length; i++) { 
   currentLabel.addMouseListener(new MouseAdapter(e) {
      public void mouseClicked(MouseEvent me) {
         JLabel label = (JLabel) me.getSource();
      }
   });
}
Devesh Jadon
  • 7,004
  • 4
  • 22
  • 27
0

This happens because label is not specified as final.

Declare the array of labels as:

final JLabel[] label;

instead of:

JLabel[] label;

Your MouseAdapter is not an inner class; it's an anonymous class. Anonymous classes can only refer to final variables of their enclosing code.

Isaac
  • 16,458
  • 5
  • 57
  • 81
  • 1
    Actually, it's an *anonymous inner class*, anonymous meaning it doesn't have an explicit name, and inner meaning it's contained in another class. – Jorn Oct 10 '10 at 19:01
0

Anonymous inner classes may only access variables of the enclosing method that are final.

meriton
  • 68,356
  • 14
  • 108
  • 175