简体   繁体   中英

java: Is it possible to set a lambda expression for an array of Buttons is a for loop? If so how?

I want to be able to do something like this:

for(i = 0; i < 10; i++) {
    //if any button in the array is pressed, disable it.
    button[i].setOnAction( ae -> { button[i].setDisable(true) } );
}

However, I get a error saying "local variables referenced from a lambda expression must be final or effectively final" . How might I still do something like the code above (if it is even possible)? If it can't be done, what should be done instead to get a similar result?

As the error message says, local variables referenced from a lambda expression must be final or effectively final ("effectively final" meaning the compiler can make it final for you).

Simple workaround:

for(i = 0; i < 10; i++) {
    final int ii = i;
    button[i].setOnAction( ae -> { button[ii].setDisable(true) } );
}

Since you are using lambdas, you can benefit also from other features of Java 8, like streams.

For instance, IntStream :

A sequence of primitive int-valued elements supporting sequential and parallel aggregate operations. This is the int primitive specialization of Stream.

can be used to replace the for loop:

IntStream.range(0,10).forEach(i->{...});

so now you have an index that can be used to your purpose:

IntStream.range(0,10)
         .forEach(i->button[i].setOnAction(ea->button[i].setDisable(true)));

Also you can generate a stream from an array:

 Stream.of(button).forEach(btn->{...});

In this case you won't have an index, so as @shmosel suggests, you can use the source of the event:

Stream.of(button)
          .forEach(btn->btn.setOnAction(ea->((Button)ea.getSource()).setDisable(true)));    

EDIT

As @James_D suggests, there's no need of downcasting here:

Stream.of(button)
      .forEach(btn->btn.setOnAction(ea->btn.setDisable(true)));

In both cases, you can also benefit from parallel operations:

IntStream.range(0,10).parallel()
         .forEach(i->button[i].setOnAction(ea->button[i].setDisable(true)));

Stream.of(button).parallel()
          .forEach(btn->btn.setOnAction(ea->btn.setDisable(true)));

Use the Event to get the source Node .

for(int i = 0; i < button.length; i++)
{
     button[i].setOnAction(event ->{
         ((Button)event.getSource()).setDisable(true);
     });
}

Lambda expressions are effectively like an annonymous method which works on stream. In order to avoid any unsafe operations, Java has made that no external variables which can be modified, can be accessed in a lambda expression.

In order to work around it,

final int index=button[i]; And use index instead of i inside your lambda expression.

You say If the button is pressed, but in your example all the buttons in the list will be disabled. Try to associate a listener to each button rather than just disable it.

For the logic, do you mean something like that :

Arrays.asList(buttons).forEach(
        button -> button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                button.setEnabled(false);

            }
}));

I Also like Sedrick's answer but you have to add an action listener inside the loop .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM