java: 是否可以将按钮数组的 lambda 表达式设置为 for 循环?如果是这样怎么办?

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

我希望能够做这样的事情:

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

但是,我收到一条错误消息 "local variables referenced from a lambda expression must be final or effectively final"。我怎么可能仍然像上面的代码那样做(如果可能的话)?如果做不到,应该怎么做才能得到类似的结果?

如错误消息所述,从 lambda 表达式引用的局部变量必须是最终的或实际上是最终的("effectively final" 意味着编译器可以为您使其成为最终的)。

简单的解决方法:

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

由于您使用的是 lambda,因此您还可以受益于 Java 8 的其他功能,例如流。

例如,IntStream:

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

可以用来代替for循环:

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

现在您有了一个可用于您目的的索引:

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

您还可以从数组生成流:

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

在这种情况下,您将没有索引,因此正如@shmosel 所建议的,您可以使用事件源:

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

编辑

正如@James_D所建议的,这里不需要向下转换:

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

在这两种情况下,您还可以从并行操作中获益:

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)));

使用 Event 获取来源 Node

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

Lambda 表达式实际上类似于在流上运行的匿名方法。为了避免任何不安全的操作,Java 规定在 lambda 表达式中不能访问任何可以修改的外部变量。

为了解决这个问题,

final int index=button[i]; 并在 lambda 表达式中使用索引而不是 i

你说如果按下按钮,但在你的示例中,列表中的所有按钮都将被禁用。尝试为每个按钮关联一个侦听器,而不是仅仅禁用它。

对于逻辑,你的意思是这样的:

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

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

            }
}));

我也喜欢 Sedrick 的回答,但你必须在循环中添加一个动作侦听器。