Referring to non-final local variable i...

Here is all of the code with comments trying to explain a few things so it's easier to read through :)

Thanks to @TfGuy44 and @GoToLoop I have some Awesome button-code that I use frequently :D. But an error occurred when I was trying to compromise it: "Cannot refer to the non-final local variable i defined in an enclosing scope".

I use @ Override to define different results for each button and doing this works:

observasjoner = new Button(300, funcY, funcW, funcH, "s", "Observasjoner", gra, gra1, gra2 ) {
      @ Override public void onClick() {
        if ( observasjoner.isOver()&& !observasjoner.ignore) layout.observer = true;
      }
    };

But when I tried to compress 10 buttons into a for loop I got the error mentioned above. This is the problematic code:

//---Problematic code---\\
      obs[i] = new Button(x, y + (i*50), funcW, plussH, "", obsN[i], pluss, pluss1, pluss2) { // ERROR
        @ Override public void onClick() {
          if (obs[i].isOver()&& !obs[i].ignore) println(obsN[i]);
        }
      };

I tried googling the error, but all I found out was that java cannot refer to non-final var accessed from inner class. I am not using a super class or extending anything, so I don't think it's relevant, but I thought I'd just mention it. I also tried writing final obs[i], Processing answered with expecting "class", so I tried final class obs[i], but that just brought more confusion...

All help is welcome.

Answers

  • Where did you define i?

  • edited February 2016

    Line 4 seems wrong

  • Sorry! I forgot the for-loop lol!

    for (int i = 0; i<num; i++) {
          int x = obsX;
          int y = obsY;
          if (i==6) {
            x = 230;
            y = obsY;
          }
           obs[i] = new Button(x, y + (i*50), funcW, plussH, "", obsN[i], pluss, pluss1, pluss2) {
            @ Override public void onClick() {
              if (obs[i].isOver()&& !obs[i].ignore) println(obsN[i]);
            }
          };
    

    Line 4? The method isOver checks if the mouse is over the button and obs[i].ignore is a boolean that checks if I should ignore the button, so it have to be false for the code to run.

  • edited February 2016 Answer ✓

    Contrary to JS, Java's half-baked closure cannot access non-final local variables! ~:>
    They must be declared as final. That doesn't apply to Java 8+.
    However they still must behave as such: =;

    Here's my mock-up, in which I declare a temporary final variable ii and assign current value of iterator i to it: :ar!

    // forum.Processing.org/two/discussion/15202/can-t-find-error
    // 2016-Feb-29
    
    static final int QTY = 10;
    final Button[] obs = new Button[QTY];
    final int[] obsN = new int[QTY];
    
    int obsX, obsY, funcW, plussH, pluss, pluss1, pluss2;
    
    void setup() {
      for (int i = 0; i < QTY; ++i) {
        final int x = i == 6? 230 : obsX, y = obsY + i*50;
        final int ii = i;
    
        obs[i] = new Button(x, y, funcW, plussH, "", obsN[i], pluss, pluss1, pluss2) {
          @ Override public void onClick() {
            if (obs[ii].isOver() && !obs[ii].ignore)  println(obsN[ii]);
          }
        };
      }
    }
    
    abstract class Button {
      int x, y, w, h;
      int a, b, c, d;
      String title;
      boolean ignore;
    
      Button(int xx, int yy, int ww, int hh, String t, int aa, int bb, int cc, int dd) {
        x = xx;
        y = yy;
        w = ww;
        h = hh;
    
        title = t;
    
        a = aa;
        b = bb;
        c = cc;
        d = dd;
      }
    
      abstract void onClick();
    
      boolean isOver() {
        return random(1)< .5;
      }
    
      @ Override String toString() {
        return "( "
          + x + ", " + y + ", " + w + ", " + h + ", " + title + ", "
          + a + ", " + b + ", " + c + ", " +d
          + " )";
      }
    }
    
  • edited February 2016

    But I've gotta warn you that it is very bad programming to force any instance of some class to access its own container! #-o

    Rather than: if (obs[ii].isOver() && !obs[ii].ignore) println(obsN[ii]);
    Just go w/: if (isOver() && ignore) println(a);

  • edited February 2016

    Thank you! That works perfectly!

    Oh... Yeah, for some reason I have "realised" that I have to do obs[ii].isOver(). Thanks for pointing it out :)

  • edited February 2016

    ... that I have to do obs[ii].isOver(). Thanks for pointing it out.

    I had no idea what you have done or haven't! I did isOver() just for the sake to have my mock-up able to run in order to test that the final "fix" was indeed working. :(|)

  • Rather than: if (obs[ii].isOver() && !obs[ii].ignore) println(obsN[ii]); Just go w/: if (isOver() && ignore) println(a);

    I was just saying I thought I had to write the first code. I understand now that I can use the one you recommended. ;)

Sign In or Register to comment.