代码之家  ›  专栏  ›  技术社区  ›  Roy Ash

添加任意数量的类变量

  •  -2
  • Roy Ash  · 技术社区  · 6 年前

    我想总结一下 Number Integer 我只想用 .intValue() 方法而不是 .doubleValue e、 x.)我不想使用 instanceof .

    返回值必须为 数字 类型

    我怎样才能用双重分派或策略模式或类似的东西来实现呢?

    我不能扩展每个实现类 数字 我不能求二的和 数字 瓦尔斯。

    只有6个 .xValue() 方法 数字 我想相应地使用它们中的每一个。

    2 回复  |  直到 6 年前
        1
  •  5
  •   Holger    6 年前

    因为实际返回的类型与调用者相关,并且由于声明的 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);
    

    注意Javas 数字 类型层次结构不反映基元类型的类型转换规则。所以当 int long 值可以作为 长的 而混合 智力 double 需要使用 双重的 为了防止精度损失,混合和混合之间没有区别 Integer 长的 与混合 整数 双重的 ,两者都只是不同文化的混合体 数字 亚型。所以无论哪种情况,你都需要 Number::xxxValue 在两者之间转换,无论实际组合如何,任何 编号:xxxValue 转换将在没有警告的情况下编译,即使它意味着精度损失。

    自大 长的 值在转换为时可能会失去精度 双重的 最后一个例子使用了一个中间词 String 价值,以确保 长的 双重的 输入值,所有转换为 BigDecimal 它们是无损的。

        2
  •  1
  •   Roland    6 年前

    总结一下评论/聊天中讨论的内容。

    免责声明: 不要在生产中使用此代码,或者只是确保理解它真正的功能。这决不是解决方案的方向或最佳实践。归还 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
    

    请注意,还有其他星座会导致错误的结果。再说一次:这有助于获得 Integer 两个双值求和之后?正如霍尔格在评论中所说:

    基于结果值的选择仅适用于声明的返回类型 数字 ,因此调用者甚至不会注意到正在更改的类型,这将导致问题而没有好处。想想 Number n1 = 0.5 + 0.5, n2 = sum(0.5, 0.5); 哪里 n1.equals(n2) 就会让步 false 1 ( 整数 )不等于 1.0 ( Double ).