1. The intention of the code is obscured by more layers of abstraction, increasing complexity.
2. Small changes to what the code is conceptually doing tend to lead to larger changes to the actual code than with imperative code.
For the first point, reading through this post and the previous post linked, the code is doing the following:
Taking the entries of a map of product to count. Turning those entries into a different class, a row of each product and count. Then it collects this list on one hand. On the other hand, it sums the total, based on the per product cost and the count of the product in the cart. Then it combines the row list and the total cost into one object, returning it.
Deconstructed, half of this code is just unnecessary complexity. A map of products to count, and a list of unique product/count tuples are theoretically identical. You can iterate over them, find specific products, etc. There /might/ be a reason to specifically desire a list; However why would that code be coupled with code to sum the cost of the cart?
All in all, why is the code not just:
public BigDecimal sumPrice(Cart cart) {
BigDecimal sum = BigDecimal.ZERO;
for (Map.Entry<Product,Integer> entry : cart.getProducts.entrySet()) {
sum = sum.add(entry.getKey().getPrice().multiply(new BigInteger(entry.getValue())
}
return sum;
}
For the second point, briefly: Consider how code would have to change to calculate a deal, such as Buy One Get One Free. Such code to do this calculation would add another layer to the collector with another function defined somewhere (or hidden in some other existing abstraction, such as OP's CartRow::getRowPrice) instead of just visible in the function that calculates a cart subtotal. If the deal relied upon concepts not limited to one row at a time, eg buy any two flavors of chips for %25 off, the proposed solution would have to be completely rewritten.