0

I've got a function like the following where I need to update a variable from inside lambda (This is the minimal viable example I could think of without giving our private info so it might not make much sense, but the lambda and stage variables are there).

But as expected, I can't update the variable since I get the error Variable used in lambda expression should be final or effectively final. What is the best way to update an external variable from within the lambda. The purpose of the variable is to know which stage the process failed in case something goes wrong, which means it can fail without returning anything.

(() -> {

            stage = "Validating";
            //do something
            stage = "Creating x";
            // do something
            stage = "Creating y";
            // .....
            stage = "finalising";
            // do something
        })
S.Dan
  • 1,826
  • 5
  • 28
  • 55
  • 1
    "Best way" is hard to know without more context. You might find a tutorial on lambdas and pick what is closest to your use case and ask about that, if you can't reveal any information. – markspace Mar 30 '23 at 22:23
  • You can try to create an object and pass it to the lambda, inside the lambda, you can change its states – 0xh3xa Mar 30 '23 at 22:27
  • @0xh3xa but the object has to be final right, which means I can't change it's states from within lambda right? – S.Dan Mar 30 '23 at 22:28
  • 3
    @S.Dan as per [Trying my best's answer](https://stackoverflow.com/a/75893448/256196), the reference to `AtomicObject` is *effectively final* (it doesn't change), but its *value* can be changed. – Bohemian Mar 30 '23 at 22:32
  • 3
    Wrong. An argument of 'final Thing thing' simply means the value of 'thing' (a reference) cannot be changed. The Thing itself can. As usual, it hinges on understanding the difference between varaibles and objects. – Arfur Narf Mar 30 '23 at 22:33
  • @S.Dan The reference object refers to the same object, which will not change, but its internal state can. for example, you have a class called `Myobject` that contains `String stage` inside Lambda you can change `stage` – 0xh3xa Mar 30 '23 at 22:42
  • Keep in mind, that when you access `stage` outside of the lambda (say, after `})` line), it will not have the value you may expect. – OneCricketeer Mar 30 '23 at 22:42
  • 1
    That comment is potentially true, and the reason why more context is needed. All of the answers (and comments) provide ways to reinvent `Future`, or method return values, or exception processing. With the expected caveats that it won't work as well as any of those. That's why we need more info on what is being done so we can direct the OP to a proper answer. – markspace Mar 30 '23 at 23:07

2 Answers2

1

You can use Atomic variables:

AtomicRefence<String> string = new AtomicReference<>();
string.set("Validating");

// some code 

string.get();

more info here -> atomic

Feel free
  • 758
  • 5
  • 15
0

Generally, I recommend you use callbacks

interface StageListener {
  void onStateChange(String state);
}

Then implement that in some "State Machine" logic.

class MyStateMachine implements StageListener {
  public void onStateChange(String stage) {
    switch(stage) {
      case "Validating":
        // some code
        break;
      default:
        break;
    }
  }
}
final StageListener machine = new MyStateMachine();

foo.example(() -> {
    machine.onStateChange("Validating");
    /// some code
    machine.onStateChange("Creating");
});

// This way, you do not accidentally try to use "stage" out here, where the value is not set. 

Then, ideally, you use an enum, not a plain string.

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245