I would like to make my code more generic. And in order to achieve that, I proved as method parameters both a list and a supplier.
It turns out that reduce()
cannot infer specific method in case where there are multiple to choose (see screenshot below).
How do I declare the method-version I would like to use?
public BigDecimal methodCalculate(List<MyObject> list, Supplier<? extends BigDecimal> getValue) {
BigDecimal sum = list.stream()
.filter(item -> item.getPremium())
.map(item -> getValue)
.reduce(BigDecimal.ZERO, BigDecimal::add);
return sum;
}
There are two issues I see in the code:
map
operation creates a stream of Supplier
, not BigDecimal
. Probably your intention was to use the supplier:.map(item -> getValue.get())
Supplier
is of type Supplier<? extends BigDecimal>
therefore with the fix above you would have a stream Stream<? extends BigDecimal>
, not a stream Stream<BigDecimal>
.Note: that there's no need to do anything special to make the compiler resolve the method reference BigDecimal::add
because only one overloaded version of add()
is applicable based on the number of arguments. This reference should be classified as a reference to an instance method of a particular object because it represents an implementation of BinaryOperator<BigDecimal>
, which expects two arguments of type BigDecimal
, i.e. the method would be invoked on the first parameter of the operator and the second would serve as an argument. And there's only one version of add()
expecting a single argument, the flavor of add()
that takes a MathContext
as an additional parameter isn't applicable here (for more information on method references, see).
But although method add()
is accessible with the hypothetical subtype of BigDecimal
and compiler is capable to resolve the required version of add()
the code would not compile.
The flavor of reduce reduce(identity,accumulator)
should have exactly the same return type (which is the same as the identity type) as the type of elements in the stream. But based on the provided identity return type of reduce
would be BigDecimal
, meanwhile in the stream we have ? extends BigDecimal
. That would cause a compilation error.
We can fix both bullet-points by changing the type of supplier to Supplier<BigDecimal>
, since there are no subtypes of BigDecimal
in JDK:
public BigDecimal methodCalculate(List<MyObject> list,
Supplier<BigDecimal> getValue) {
return list.stream()
.filter(item -> item.getPremium())
.map(item -> getValue.get())
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
If you can't change the type of the supplier, then you need another flavor of reduce(identity,accumulator,combiner)
, which is capable to have an identity (i.e. return type as well) that differs from the type of elements in the stream:
public BigDecimal methodCalculate(List<MyObject> list,
Supplier<? extends BigDecimal> getValue) {
return list.stream()
.filter(item -> item.getPremium())
.map(item -> getValue.get())
.reduce(BigDecimal.ZERO, BigDecimal::add, BigDecimal::add);
}