-1

Create multiple new Runnable inside a loop ?

I want to create multiple new instances of Runnable that calls another method and passing the "i" parameter from the parent's loop.

I do not want to use Threads since those runnables will be used as actions for keyboard shortcuts (using KeyCombination and accelerators). (ex: Ctrl + 1, Ctrl + 2, ... Ctrl + 9)

The content of the runnable that I want looks like this :

if (shortcutsEnabled && checkDestinationValidity(i)) {
    copyImage(i);
}

possibly important : the number of "i" will go up to 9.

Is there any way to do this without having to make a premade table of Runnable ?

When I try to just put the variable inside, the NetBeans IDE shows me this message : local variables referenced from a lambda expression must be final or effectively final

I have found a solution that has delayed my "deadline" for the solution :

for (int i = 0; i < copyKeyCombinations.size(); i++) {
    final int index = i;
    Runnable copyRunnable = ()-> {
        if (shortcutsEnabled && checkDestinationValidity(index)) {
            copyImage(index);
        }
    };
    accelerators.put(copyKeyCombinations.get(i), copyRunnable);
}

For the record, the runnable are registered before the loop ends, and the solution above works but I was asked to find another solution

However, this is a solution that is not accepted for my work situation, I am looking for any alternatives.

(Note : this loop will only be launched one time, at the start of the app).

Juraon
  • 11
  • 2
  • I think the above solution also wouldn't work. Does it work, even if it is not accepted? I think `Runnable copyRunnable` object is created inside that for loop, but that it is not attached anywhere. So, the Lambda passed to it won't be called from anywhere, just created and then garbage collected as `copyRunnable` goes out of the scope (next iteration of the loop). – Ishan May 25 '23 at 11:20
  • You have to register those `Runnable` objects somewhere, and when the keyboard shortcut is pressed, that event handler should trigger it. You are not storing those `Runnable` objects anywyere, those are just created and forgotten. – Ishan May 25 '23 at 11:22
  • Another way is to have that lambda in the key-pressed event handler itself. Based on the key pressed, you will get a single `index` - if it is valid, call `copyImage(index)` – Ishan May 25 '23 at 11:25
  • 3
    Extract the `Runnable` creating into its own method. There should be no problem using an effectively final parameter. – Johannes Kuhn May 25 '23 at 11:31
  • 2
    "However, this is a solution that is not accepted for my work situation, ..." - and **why** is it not accepted? It does not make sense to suggest alternative solutions without knowing what you are not allowed to do. – Thomas Behr May 25 '23 at 12:02
  • Also consider a `Service`, illustrated [here](https://stackoverflow.com/q/73528613/230513). – trashgod May 25 '23 at 12:25

1 Answers1

0

You can implement Runnable the "standard" way, as a class, not as lambda function:

public class CopyRunnable implements Runnable {

  private final int index;
  private final boolean shortcutsEnabled;

  public CopyRunnable(int index, boolean shortcutsEnabled) {
    this.index = index;
    this.shortcutsEnabled = shortcutsEnabled;
  }

  @Override
  public void run() {
    if (shortcutsEnabled && checkDestinationValidity(index)) {
      copyImage(index);
    }
  }
}
for (int i = 0; i < copyKeyCombinations.size(); i++) {
  Runnable copyRunnable = new CopyRunnable(i, shortcutsEnabled);
  //do something with it
}
Chaosfire
  • 4,818
  • 4
  • 8
  • 23
  • Here also, `Runnable copyRunnable` is created and then forgotten (garbage collected) as soon as the next iteration of the for loop executes. The `copyRunnable` has to be registered somewhere, which gets called when the key-pressed event is raised and is valid Ctrl+1 to Ctrl+2. There is no need to have a `for` loop at all. When that `index` is received in the key-pressed event, Runnable can be created and called. – Ishan May 25 '23 at 11:30
  • Yep, now modified with `//do something with it` - that is important. That `copyRunnable` has to be registered somewhere, which will be used when the key-pressed event is raised. As such, there is no need to pre-register everything in a for loop in advance. The logic will remain the same, just be called when event is raised. – Ishan May 25 '23 at 11:32
  • I just added `accelerators.put(copyKeyCombinations.get(i), copyRunnable);` at the end of the loop and the solution worked perfectly – Juraon May 25 '23 at 11:43
  • 1
    @Ishan The question is specifically how to deal with the requirement for lambdas - `local variables referenced from a lambda expression must be final or effectively final`. It is assumed that OP is doing something with the created `Runnable`s, including the code for this is absolutely redundant both for question and answer. – Chaosfire May 25 '23 at 11:54