Kefirchiks
Kefirchiks

Reputation: 288

Java 8 lambda aggregate

I'm having a small problem with lambdas. How can I iterate through it and add value every iteration to my variable currentLateAmountDouble?

BigDecimal currentLateAmountDouble = BigDecimal.ZERO;
Collection<VPO> lines = BO_Contract.getLines(trxName, contractVPO.getID(), RContractLine.LineNo + " ASC ", ctx);
if (lines != null && !lines.isEmpty()) {
    if (lines.stream()
             .filter(line -> onDate.compareTo(line.getDateValue(RContractLine.TermDate)) > 0) != null) {
        lines.forEach(line -> currentLateAmountDouble = currentLateAmountDouble.add(NumberUtils.getAmount(line.getDoubleValue(RContractLine.TotalAmountOpen))));
    }
}

It gives me an error on the 5th line:

Local variable currentLateAmountDouble defined in an enclosing scope must be final or effectively final

The same method in Java 7 (works):

BigDecimal currentLateAmountDouble = BigDecimal.ZERO;
Collection<VPO> lines = BO_Contract.getLines(trxName, contractVPO.getID(), RContractLine.LineNo + " ASC ", ctx);
if (lines != null && !lines.isEmpty()) {
    for (VPO line : lines) {
        if (onDate.compareTo(line.getDateValue(RContractLine.TermDate)) > 0) {
            currentLateAmountDouble = currentLateAmountDouble.add(NumberUtils.getAmount(line.getDoubleValue(RContractLine.TotalAmountOpen)));
        }
    }
}

EDIT: Solved! Went with @JBNizet solution and did it like this:

currentLateAmountDouble = lines.stream().filter(line -> onDate.compareTo(line.getDateValue(RContractLine.TermDate)) > 0)
                .map(line -> NumberUtils.getAmount(line.getDoubleValue(RContractLine.TotalAmountOpen))).reduce(BigDecimal.ZERO, BigDecimal::add);

Upvotes: 0

Views: 644

Answers (2)

JB Nizet
JB Nizet

Reputation: 691775

As the error message says, you may not reassign an external local variable from a lambda (just as from an anonymous class, in Java 7).

Trying to modify an external state from functional operations is a code smell. But your operation is just a mapping, followed by a reduction operation: you're transformaing each element to a BigDecimal, and then sum all the BigDecimals:

BigDecimal currentLateAmountDouble = 
   lines.stream()
        .map(line -> NumberUtils.getAmount(line.getDoubleValue(RContractLine.TotalAmountOpen)))
        .reduce(BigDecimal.ZERO, BigDecimal::add);

Note that your test that compares the result of filter() with null doesn't make sense: filter() will never, ever return null. You're probably looking for anyMatch here.

Upvotes: 3

AAG
AAG

Reputation: 245

You need to use a reducing function...

try this in your if statement...

currentLateAmountDouble  = lines.stream().mapToInt(l->NumberUtils.getAmount(l.getDoubleValue( RContractLine.TotalAmountOpen)).sum();

it may not be exact, i'm not by a computer to test this out, but should get you close enough.

also, see https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html for reference

Upvotes: 1

Related Questions