由于实际返回的类型与调用者相关,并且由于声明的类型为Number 而对调用者没有太大用处,因此它应该在调用者的控制之下并与泛型结合类型签名,它允许调用者实际使用特定的返回类型。例如
public static <N extends Number, R extends Number> R sum(
List<? extends N> input, Function<? super N, ? extends R> cast,
BinaryOperator<R> addition) {
return input.stream().<R>map(cast).reduce(addition).orElse(null);
}
public static <N extends Number> N sum(
List<? extends N> input, BinaryOperator<N> addition) {
return sum(input, Function.identity(), addition);
}
这允许请求计算在输入类型内,例如
List<Integer> list = Arrays.asList(1, 2, 3, 4);
Integer iSum1 = sum(list, Integer::sum);
Integer iSum2 = sum(list, Math::addExact);//throw on overflow
还要在总结之前拓宽类型:
Long lSum = sum(list, Integer::longValue, Long::sum);
同样,您可以处理Long 或Double 输入类型:
List<Long> list = Arrays.asList(1L, 2L, 3L, 4L);
Long lSum1 = sum(list, Long::sum);
Long lSum2 = sum(list, Math::addExact);//throw on overflow
// without precision loss:
BigInteger biSum = sum(list, BigInteger::valueOf, BigInteger::add);
List<Double> list = Arrays.asList(1.0, 2.0, 3.0, 4.0);
Double dSum = sum(list, Double::sum);
// without precision loss:
BigDecimal bdSum = sum(list, BigDecimal::valueOf, BigDecimal::add);
或者处理混合类型:
List<Number> list = Arrays.asList(1, 2L, 3.0, 4F);
Double dSum = sum(list, Number::doubleValue, Double::sum);
BigDecimal bdSum = sum(list, n -> new BigDecimal(n.toString()), BigDecimal::add);
请注意,Java 的 Number 类型层次结构不反映原始类型的类型转换规则。因此,虽然 int 和 long 值的混合可以处理为 long 而混合 int 和 double 需要使用 double 以防止精度损失,混合 Integer 之间没有区别和Long 与混合Integer 和Double,两者都只是不同Number 子类型的混合。因此,无论哪种情况,您都需要在两者之间进行Number::xxxValue 转换,并且无论实际组合如何,任何Number::xxxValue 转换都将在没有警告的情况下编译,即使它意味着精度损失。
由于较大的long 值在转换为double 时可能会丢失精度,所以最后一个示例使用中间的String 值,以确保在存在long 和double 输入值的情况下,所有到BigDecimal 的转换是无损的。