1

I need to change a local variable inside a lambda event handler in .

SerialPort comPorts[] = SerialPort.getCommPorts();
MenuItem[] portsItems = new MenuItem[10];
int q=0;
   for (SerialPort port : comPorts) {
       portsItems[q] = new MenuItem(port.getSystemPortName());
       portsItems[q].setOnAction(actionEvent -> {
               portNum = q;
               connect.setDisable(false);
       });
       comPortsMenu.getItems().add(portsItems[q]);
   }

The problem is I need to increment q in each loop, but I can't do that because q must be final or effectively final to be used inside a lambda.

Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89
a.ghad
  • 13
  • 6
  • @AndrewTobilko You quoted his exact words. – Yassin Hajaj Mar 05 '16 at 12:05
  • Make `q` a class instance field. – Hovercraft Full Of Eels Mar 05 '16 at 12:11
  • 2
    Where are you trying to increment the value? If you just need to do it "in the loop", you can do that without doing it in the lambda expression. If you need a final variable that has the value of q, just copy it: `final int pNumber = q;` and then `portNum = pNumber;` will work. – James_D Mar 05 '16 at 12:12
  • Possible duplicate of [Java lambda - for loop counter is not effectively final](http://stackoverflow.com/questions/24893597/java-lambda-for-loop-counter-is-not-effectively-final) – fabian Mar 05 '16 at 13:40

3 Answers3

2

I think you are looking for something like

for (SerialPort port : comPorts) {
    portsItems[q] = new MenuItem(port.getSystemPortName());
    int portNumber = q ; // effectively final
    portsItems[q].setOnAction(actionEvent -> {
        portNum = portNumber;
        connect.setDisable(false);
    });
    comPortsMenu.getItems().add(portsItems[q]);

    // increment:
    q++ ;
}
James_D
  • 201,275
  • 16
  • 291
  • 322
  • I think he wanted to change the variable inside the lambda body, otherwise he didn't have the compile error. – Andrew Tobilko Mar 05 '16 at 12:32
  • @AndrewTobilko Not true. `q++` writes to `q` therefore `q` is not effectively final. @James_D you could make that variable `final`. There's no point in keeping it just effectively final here. – fabian Mar 05 '16 at 12:33
  • it worked thanks man:) can u explain me how u fixed it and why we have to use final in lambda? – a.ghad Mar 05 '16 at 12:41
  • 1
    @a.ghad : lambda/anonymus classes can only access effectively final/final variables (language design choice; you wouldn't want to access the value once it's at `comPorts.length` BTW). Copying the variable value to a new variable inside the loop body means it's value of the new variable doesn't need to be changed and therefore it can be kept effectively final. But you should add a `final` to the variable declaration since this adds compile time / IDE checks and also the effect should be clear without adding a comment. – fabian Mar 05 '16 at 13:20
0

You can do an tab of integer and use the first index to increment. In lambda variables are captured, so references have to be final. So reference can't be replaced by an other. It is for that we sould use an array of an object contening the int value.

Damien Chesneau
  • 457
  • 2
  • 18
0

If you are open to using apache commons, the Mutable* family of classes provide a good mechanism for closure-friendly 'boxing' of values, since the capital-letter boxes provided by the framework do not.

For the case above, replacing q with a MutableInt would work.

That said, with this particular example, you could potentially get a better result by using a (variable length) list for portsItems, and a combination of add() and size() - 1 for the corresponding 'port' value.

J. Paulding
  • 494
  • 3
  • 9