我想对Number
对象的列表求和,并且每个对象仅使用实数值(如果它是Integer
,我只想使用.intValue()
方法而不是{{1 }}例如。),我不想使用.doubleValue
。
返回值必须为instanceof
类型。
我该如何使用双重调度或策略模式或类似方法?
我无法扩展Number
的每个实现类,也不能对两个Number
变量求和。
Number
中只有6个.xValue()
方法,我想相应地使用它们。
答案 0 :(得分:5)
由于实际返回的类型与调用者相关,并且由于声明的类型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
的所有转换都是无损的。
答案 1 :(得分:1)
仅是总结评论/聊天中讨论的内容。
免责声明::请勿在生产中使用此代码,或者只是确保了解它的实际作用。绝不是解决方案或最佳实践。返回Number
的类型已被改编且呼叫者未意识到的迟早会给您带来麻烦。如果您想以呼叫者友好的方式解决问题,请查看Holgers answer。这里的答案只是按照他的要求解决了OP的问题。它没有任何真正的好处。基本上,这里只是说明按照请求的方式解决它可能是一个坏主意;-)。话虽如此,让我们开始...
一种定义策略的方法:
class Strategy {
Predicate<Number> predicate;
UnaryOperator<Number> transformation;
Strategy(Predicate<Number> predicate, UnaryOperator<Number> transformation) {
this.predicate = predicate;
this.transformation = transformation;
}
boolean applies(Number number) {
return predicate.test(number);
}
Number transformNumber(Number number) {
return transformation.apply(number);
}
}
然后可能的策略列表如下
List<Strategy> strategies = Arrays.asList(
new Strategy(n -> n.byteValue() == n.doubleValue(), Number::byteValue),
new Strategy(n -> n.shortValue() == n.doubleValue(), Number::shortValue),
new Strategy(n -> n.intValue() == n.doubleValue(), Number::intValue),
new Strategy(n -> n.longValue() == n.doubleValue(), Number::longValue), // please read the disclaimer again...
new Strategy(n -> n.floatValue() == n.doubleValue(), Number::floatValue), // please spare your comments :-)
new Strategy(n -> true, Number::doubleValue) // ... lets continue!
);
一个简单的总结和策略的应用:
Optional<Number> sum(Number... numbers) {
return Arrays.stream(numbers)
.reduce(this::sumBasedOnStrategy);
}
Number sumBasedOnStrategy(Number one, Number two) {
Number result = one.doubleValue() + two.doubleValue();
return strategies.stream()
.filter(s -> s.applies(result))
.map(s -> s.transformNumber(result))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No known strategy for the given number"));
}
现在测试求和策略:
Stream.of(1, 256, 66000, 3000000000L, 1.1f, 3.4f, Double.MAX_VALUE)
.map(n -> sum(n, 1))
.map(Optional::get)
.map(Object::getClass)
.forEach(System.out::println);
您期望什么?
class java.lang.Byte
class java.lang.Short
class java.lang.Integer
class java.lang.Long
class java.lang.Double // really?
class java.lang.Float
class java.lang.Double
这是对应的总和结果...
2
257
66001
3000000001
2.100000023841858 // thank you for your attention ;-)
4.4
1.7976931348623157E308
请注意,还有其他星座会导致错误的结果。再说一次:将两个double值相加后得到Integer
有什么帮助?正如Holger在他的评论(引用)中所示:
基于结果值的选择仅适用于声明的返回类型为
Number
,因此调用者甚至不会注意到更改的类型,这将导致问题,而无益。想想Number n1 = 0.5 + 0.5, n2 = sum(0.5, 0.5);
,其中n1.equals(n2)
会产生false
的原因是1
(Integer
)不等于1.0
(Double
)。