首页 > 解决方案 > 添加任意 Number 类变量

问题描述

我想总结一个Number对象列表,并且每个对象只使用实际值(如果它是一个Integer我只想使用.intValue()方法而不是.doubleValueex..),我不想使用instanceof.

返回值必须是Number类型。

我如何使用双重调度或策略模式或类似的东西来做到这一点?

我不能扩展每个实现类,Number也不能对两个Number变量求和。

里面只有 6.xValue()种方法Number,我想相应地使用它们中的每一种。

标签: javajava-11

解决方案


由于实际返回的类型与调用者相关,并且由于声明的类型为 而不明显时对调用者没有多大用处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);

同样,您可以处理LongDouble输入类型:

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类型层次结构并不反映原始类型的类型转换规则。因此,虽然intlong值的混合可以处理为whilelong混合int并且double需要使用以防止精度损失,但混合和与混合和double之间没有区别,两者都只是不同子类型的混合。因此,无论哪种情况,您都需要在两者之间进行转换,无论实际组合如何,任何转换都将在没有警告的情况下编译,即使它意味着精度损失。IntegerLongIntegerDoubleNumberNumber::xxxValueNumber::xxxValue

由于大long值在转换为 时可能会丢失精度double,因此最后一个示例使用中间String值,以确保在存在longdouble输入值的情况下,所有转换为BigDecimal无损。


推荐阅读