0

I have a for loop as below which is working fine.

try {
    Accountant accountant = getAccountant(employees);
    for (Employee person : employees) {
        accountant.pay(person, this); // throws BudgetIsOverException
    }
    allSalariesPaid = true;
}
catch (BudgetIsOverException e){
    allSalariesPaid = false;
}

But when I use a stream API instead, it asks me to handle the BudgetIsOverException and shows an error at accountant.pay even though it is properly caught after the try statement. Below the code:

try {
    Accountant accountant = getAccountant(employees);
    employees.stream()
             .forEach(e->accountant.pay(e,this)); // throws BudgetIsOverException
    allSalariesPaid = true;
}
catch (BudgetIsOverException e) {
    allSalariesPaid = false;
}

My question is why is stream API expecting an exception to be caught inside the foreach block?

Holger
  • 285,553
  • 42
  • 434
  • 765
antnewbee
  • 1,779
  • 4
  • 25
  • 38
  • 6
    Exceptions thrown from within a lambda must be handled from within the expression. They cannot "escape" the lambda and be handled in the outer scope. Please see: https://stackoverflow.com/questions/31637892/throwing-exception-from-lambda/31638189 – HomeworkHopper Jul 20 '23 at 17:43
  • 1
    Most of the functional interfaces provided by JSE don't support checked exceptions (Callable is one of the few that does). Unchecked exceptions can escape lambdas, but if a method used inside a lambda can throw a checked exception, you need to catch it inside the lambda (and possibly rethrow wrapped in an unchecked exception). – Rob Spoor Jul 20 '23 at 18:31
  • might be helpful https://www.oreilly.com/content/handling-checked-exceptions-in-java-streams/ – Black Bear Jul 20 '23 at 18:53
  • 1
    I would say this code should be rewritten so that `Accountant::pay` does not throw. Going over budget is an expected situation (obviously, since you handle it) and you should not use exceptions for flow control to handle expected occurrences. – David Conrad Jul 21 '23 at 05:59
  • 3
    Letting the exception handling aside, why do you think this stream version is an improvement over the original `for` loop? – Holger Jul 21 '23 at 08:54
  • @Holger because my client think so and wouldn't approve the PR. Sometimes using stream help in clean code – antnewbee Jul 21 '23 at 09:12
  • So I fixed the issue by making BudgetOverException extend RunTimeException – antnewbee Jul 21 '23 at 09:14
  • There is no need to use `stream()` in your code. Just `employees.forEach(...)`. – k314159 Jul 21 '23 at 09:52

2 Answers2

1

The foreach construct expects a consumer object

void forEach(Consumer<? super T> action);

The abstract method of consumer is

void accept(T t);

Note the above abstract method of consumer does not throw any checked exception so you need to handle it for each employee object unlike in above foreach loop. You could just handle and rethrow runtime exception.Its not a recommended way but the above code implies you are good to exit as soon as one of the employee's pay fail so we could go ahead with it.

    private void method() {
        try {
        Accountant accountant = getAccountant(employees);
        employees.stream()
                .forEach(emp -> paySalary(accountant, emp, this));
        allSalariesPaid = true;
        }catch(RuntimeException e){
            allSalariesPaid = false;
        }
    }

    private void paySalary(Accountant accountant, Employee emp, CurrentClass currentClassRef) {
        try {
            accountant.pay(emp, currentClassRef);
        } catch (BudgetIsOverException exception) {
            throw new RuntimeException(exception);
        }
    }
Sagar
  • 104
  • 1
  • 5
0

The lambda may not throw checked exceptions.

You can achieve the same result this way (and in 1 line):

boolean allSalariesPaid = employees.stream()
    .allMatch(e -> {
        try {
            accountant.pay(e, this);
            return true;
        } catch (BudgetIsOverException ex) {  // must catch checked exceptions
            return false;
        }
    });
Bohemian
  • 412,405
  • 93
  • 575
  • 722