【问题标题】:How to handle numbers in a generic fashion?如何以通用方式处理数字?
【发布时间】:2009-09-01 00:25:30
【问题描述】:

我的问题与"Writing a generic class to handle built-in types" 非常相似,包括受到在一个类上处理矩阵运算这一事实的启发。 虽然这个问题是使用 C# 提出的,并指出了 Generic Operators 上的一篇文章。

我不明白。 Java Number 没有 add 方法,因此您可以使用如下方法:

public Number myAdd(Number a, Number b){
     return a.add(b);
}

那么,如果您希望能够在 Java 中处理多种类型的数字,您该如何处理呢?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    根本问题在于非常原始的 Java 类型系统。

    由于 Java 中不存在密封类型集的概念(Java 也无法像 Haskell 那样推断类型),因此无法制作通用的 Number + Number -> Number 而不使用技巧。

    对于原语(以及像 Integer 这样可以自动映射到它们的对象)类型提升和 + 操作是语言的一部分。 (这是问题的实际部分:在 a 和 b 属于不同类型的情况下,数字 a + 数字 b 应该返回什么?)

    如果您真的想要这种行为,您必须找到(或创建)您自己的自定义类,该类使用反射或一系列(检查和)强制转换等。即使您使用泛型(请记住,泛型是类型擦除的)也需要进行强制转换。

    我想这些问题是 Number 如此乏味的部分原因。

    【讨论】:

    • “在 a 和 b 属于不同类型的情况下 Number a + Number b 返回什么”几乎是问题的全部。 :)
    【解决方案2】:

    您希望结果有多好?如果答案是“足够好,主要是”,那么这就足够了:

    public Number myAdd(Number a, Number b){
         return a.doubleValue() + b.doubleValue();
    }
    

    但是,如果您想要一些与 Java 原语的提升语义相匹配的东西,您可能必须自己编写它。然后你必须弄清楚“非标准”Number 实现的所有组合的规则是什么,包括BigDecimalBigIntegerAtomicDoubleAtomicLongorg.apache.commons.lang.mutable 中的所有内容,以及某人可能决定在下周二编写的任何随机实现。

    目前尚不清楚在大多数情况下正确的做法是什么——例如,如果参数之一是 Apache Commons 的 Fraction.ONE_THIRD,则将所有内容都转换为 BigDecimal 是不可行的;此外,以一般方式进行转换会出现与以一般方式进行加法相同的问题。但是在Number 上使用add() 方法将需要每个Number 实现来处理所有这些情况——这可能就是它不存在的原因。

    【讨论】:

      【解决方案3】:

      我不明白。 Java 编号不 有一个 add 方法...

      假设java.lang.Number 确实有一个或多个add 方法,您将如何定义它的签名?你会如何定义它的语义?您将如何处理“混合模式”算术?

      虽然毫无疑问可以回答这些问题并设计 API,但正确使用结果可能会很棘手。此外,应用程序需要执行“表示不可知”算术是最不寻常的。通常,您希望/需要明确控制算术执行和转换发生的方式。 (Java 原始类型提升规则已经很难让人理解了!!)

      总而言之,我认为 Sun 为我们提供了很好的服务没有试图在 Number API 中支持算术。

      【讨论】:

        【解决方案4】:

        实现泛型 add 方法的一种方法是让左侧参数推断返回类型。

        package mixins;
        
        import java.math.BigDecimal;
        
        public class Numbers {
        
            public static boolean isZ(Number n) {
                return n instanceof Integer || n instanceof Long || n instanceof Short || n instanceof Byte;
            }
        
            public static boolean isR(Number n) {
                return n instanceof Double || n instanceof Float;
            }
        
            public static BigDecimal add(BigDecimal a, Number b) {
                if (b instanceof BigDecimal) {
                    return a.add((BigDecimal) b);
                } else if (isZ(b)) {
                    return a.add(new BigDecimal(b.longValue()));
                } else if (isR(b)) {
                    return a.add(new BigDecimal(b.doubleValue()));
                }
                throw new IllegalArgumentException("No valid big decimal translation for " + b.getClass());
            }
        
            public static Integer add(Integer a, Number b) {
                return a + b.intValue();
            }
        
            public static Long add(Long a, Number b) {
                return a + b.longValue();
            }
        
            public static Float add(Float a, Number b) {
                return a + b.floatValue();
            }
        
            public static Double add(Double a, Number b) {
                return a + b.doubleValue();
            }
        }
        

        如果这是作为静态方法实现的,则可以使用静态导入。

        import static mixins.Numbers.*;
        
        public class Example {
        
            public static void main(String[] args) {
                BigDecimal fortytwo = new BigDecimal(42);
                BigDecimal fiftyfive = add(fortytwo, 13);
                System.out.println(fiftyfive);
            }
        
        }
        

        【讨论】:

          【解决方案5】:

          由于其他指出的原因,您不能添加任何两个数字,但是您可以添加相同类型的数字,结果也将是相同类型。 您可以在 Java 中创建通用算术,如下所示:

          interface Arithmetics<T> {
              T add(T val1, T val2);
          }
          
          class IntegerArithmetics implements Arithmetics<Integer> {
              Integer add(Integer val1, Integer val2) { return val1 + val2; }
          }
          
          //similarly DoubleArithmetics, BigIntegerArithmetics, ...
          

          Generic Java Math 库正是为您做的。

          【讨论】:

            【解决方案6】:

            实际上,我一直在研究一个通用的“实数”类(称为“值”),但更多的是作为一种设计练习;我明白为什么没有早点完成。

            首先,您必须有一些基本规则才能使用 - 我选择使用 Java FP (IEEE-754) 规则 - 这意味着您必须允许像“无穷大”和“NaN”这样的结果,即使该类型实际上并不支持它们;事实证明,倒数之类的事情非常棘手。但我快到了,这是一段有趣的旅程。

            帮助我的一件事是尽早决定我需要处理“身份”值——特别是 0、1 和 -1,以及作为特殊情况的 -0、+/-infinity 和 NaN;原因是(例如)乘以它们中的任何一个通常根本不需要任何计算。 x * 1 = x,x * NaN = NaN,x * 0 = 0(或 NaN),并且 x * +/-infinity = +/-infinity;除法、加法和减法也有类似的规则,这意味着您可以快速、一致地消除大量渣滓。 这使得实施者只需要处理确实需要计算的情况。

            当然,并非所有类型都支持所有标识,但如果您将它们设为方法,则可以在操作数或结果“不受支持”时抛出异常。

            希望它可以帮助其他有兴趣给它一个 bash 的人,但它并不像看起来那么简单。 :-)

            【讨论】:

              【解决方案7】:

              就我个人而言,我几乎在所有事情上都使用 BigDecimals(但这主要是因为我使用的是货币价值)。它们处理任何大小的所有数值。因此,在我看来,它们是一个通用值,可以在您的假设示例中使用,而不是 Number 抽象类。 任何东西都可以变成BigDecimal,为什么不用呢?

              public BigDecimal myAdd(BigDecimal a, BigDecimal b) {
                  return a.add(b);
              }
              

              编辑:要解决下面的 BigBrothers 评论,您始终可以使用 doubleValue() 方法来创建自己的通用方法。唯一的问题是,在某些极少数情况下,当有人传入 BigDecimal 并且它大于 Double.maxValue 时,您可能会丢失精度

              public Number myAdd(Number a, Number b) {
                  return new BigDecimal(a.doubleValue() + b.doubleValue());
              }
              

              BigDecimal 是一个数字,因此返回一个没有任何意义。

              【讨论】:

              • 当我看到你在说什么时,我想我希望有更优雅的东西。
              猜你喜欢
              • 1970-01-01
              • 2014-12-19
              • 1970-01-01
              • 2012-08-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-03-18
              • 1970-01-01
              相关资源
              最近更新 更多