Which is the best way to set/drop boolean flag inside lambda function

13,036

Solution 1

You may be better off with an old-style loop, as others have suggested. It does feel like a bit of a programming faux pas to write lambdas with side-effects, but you're likely to find an equal number of developers who think it's fine too.

As for getting this particular lambda-with-side effects working, making isLoaded into an AtomicBoolean is probably your best bet. You could achieve the same effect by making isLoaded a boolean[] of size 1, but that seems less elegant than going with AtomicBoolean to me.

But seriously, try using an old-school loop instead too and see which one you like better.


If you use parallel stream, you must use AtomicBoolean. Because boolean[1] may not be safe in parallel scenario.

Solution 2

The java.util.stream javadoc states that

Side-effects in behavioral parameters to stream operations are, in general, discouraged, as they can often lead to unwitting violations of the statelessness requirement, as well as other thread-safety hazards.

That said, if you want to do it anyway, the solution you have identified with an AtomicBoolean will do the trick just fine.

Solution 3

Variables used within anonymous inner classes and lambda expression have to be effectively final.

You can use AtomicReference for your case, here is a similar snippet from ConditionEvaluationListenerJava8Test

public void expectedMatchMessageForAssertionConditionsWhenUsingLambdasWithoutAlias() { 
    final AtomicReference<String> lastMatchMessage = new AtomicReference<>(); 
    CountDown countDown = new CountDown(10); 
    with() 
            .conditionEvaluationListener(condition -> { 
                try { 
                    countDown.call(); 
                } catch (Exception e) { 
                    throw new RuntimeException(e); 
                } 
                lastMatchMessage.set(condition.getDescription()); 
            }) 
            .until(() -> assertEquals(5, (int) countDown.get())); 

    String expectedMatchMessage = String.format("%s reached its end value", CountDown.class.getName()); 
    assertThat(lastMatchMessage.get(), allOf(startsWith("Condition defined as a lambda expression"), endsWith(expectedMatchMessage))); 
} 

Cheers !

Share:
13,036
Alex Saunin
Author by

Alex Saunin

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Updated on July 29, 2022

Comments

  • Alex Saunin
    Alex Saunin over 1 year

    Say I have a currency rates loader returning isLoaded=true result only when all the rates are loaded successfully:

    //List<String> listFrom = Stream.of("EUR", "RUB").collect(toList());
    //List<String> listTo = Stream.of("EUR", "CNY").collect(toList());
    
    boolean isLoaded = true;
    
    final FixerDataProvider httpProvider = new FixerDataProvider(maxAttempts);
    final List<CurrencyRatePair> data =
        listFrom.stream()
            .flatMap(from -> {
                final List<CurrencyRatePair> result = httpProvider.findRatesBetweenCurrencies(from, listTo);
                if (Objects.isNull(result) || result.size() == 0) {
                    isLoaded = false; //!!!Not working as ineffectively final!!!
                }
                return result.stream();
            }).collect(Collectors.toList());
    
    if (!isLoaded) {
        return false;
    }
    
    // do smth with loaded data 
    
    return true;
    

    Assignment isLoaded = false; inside lambda function is not allowed when isLoaded variable is not final or effectively final.

    Which is the most elegant solution to set/drop boolean flag inside lambda expressions?

    What do you think about AtomicBoolean and set(false) method as a possible approach?

  • Alex Saunin
    Alex Saunin over 6 years
    No, I'm expecting isLoaded=false when any of results is null or empty.
  • Karl Reid
    Karl Reid over 6 years
    I think a lot of programmers find that there is a certain elegance and convenience to using the Stream API and lambdas, and prefer to use them over 'old' loops, but eventually they run into this issue with side effects, so there's a bit of a clash.
  • Alex Saunin
    Alex Saunin over 6 years
    I just wanted to update my post with additional question... Why it is forbidden to use non-effectively final variables while there're several ways to bypass this rule? But I guess you just brought me to the right answer. Is it made so to minimize possible side effects?
  • Alex Saunin
    Alex Saunin over 6 years
    Actually, I think side effects is not a definitely evil. As I do not plan to have a pure function, why not to take an advantages of the functional coding style (compactness and readability)?
  • Per Huss
    Per Huss over 6 years
    Oh, the non-effectively final variable thing has to do with that lambdas are really inner classes under the hood, and an inner class may not refer to a local non-final variable. Before Java 8 you had to explicitly declare such a variable with final, but nowadays it's enough that you treat it as a final by not changing its value, i.e. effectively final... If you use an AtomicBoolean you're not reassigning the variable, but modifying the state of the object it refers to instead, and thus treating it effectively final. Do I make sense?
  • Vitalii Muzalevskyi
    Vitalii Muzalevskyi over 6 years
    Than it means that it is bad solution to use stream in current case, there is no possibility to interapt stream by some condition, you should use loop in your case.
  • Alex Saunin
    Alex Saunin over 6 years
    Maybe it is not so bad if I want for ex. to log all the failed attempts during the loop without it's termination. And only when the loop is totally completed, than 'll make a final decision.
  • CodeBlind
    CodeBlind over 6 years
    Yeah, I'm on the fence when it comes to the relative wickedness of side effects in Java lambdas. The rule of thumb I generally use in Java is, if a lambda with side effects reduces the amount of code written AND the scope of the side effects are limited to a single method body, then it really isn't going to hurt anything. At least I haven't heard an argument against doing thing this way that didn't seem pedantic. Seems like if you're truly worried about side effects at that level, then functional Java probably isn't functional enough for you in the first place :)
  • ChumiestBucket
    ChumiestBucket over 5 years
    what is the boolean[] of size 1 solution? I'm curious
  • San
    San over 3 years
    You can use Boolean[] isLoaded = new Boolean[1]; and modify isLoaded[0] inside lambda.