【问题标题】:How to add two java.lang.Numbers?如何添加两个 java.lang.Numbers?
【发布时间】:2010-04-27 13:05:44
【问题描述】:

我有两个号码。例如:

Number a = 2;
Number b = 3;
//Following is an error:
Number c = a + b;

为什么 Numbers 不支持算术运算?无论如何我将如何在java中添加这两个数字? (当然我是从某个地方得到它们,我不知道它们是整数还是浮点数等)。

【问题讨论】:

  • 他们真的是这样分配的吗?我的意思是,数字 a = 原始数字有效吗?如果您从某个返回“something extends Number”的方法中获取它们,您实际上可以检查返回的 Number 是否是 Double 或 Float 或 BigDecimal 的实例。
  • @Tedil 是的,从 Java 1.5 开始,这种分配是可能的,这要归功于称为“自动装箱”的功能。原始数字2 是一个int,它被装箱到一个Integer,它是Number 的一个子类。

标签: java


【解决方案1】:

你说你不知道你的数字是整数还是浮点数……当你使用Number 类时,编译器也不知道你的数字是整数、浮点数还是其他东西。结果,像 + 和 - 这样的基本数学运算符不起作用。计算机不知道如何处理这些值。

开始编辑

根据讨论,我认为举个例子可能会有所帮助。计算机将浮点数存储为两部分,一个系数和一个指数。因此,在理论系统中,001110 可能被分解为 0011 10,或 32 = 9。但正整数将数字存储为二进制,因此 001110 也可能意味着 2 + 4 + 8 = 14。当您使用 Number 类时,您是在告诉计算机您不知道数字是浮点数还是整数或什么,所以它知道它有 001110 但它不知道这是否意味着 9 或 14或其他一些值。

结束编辑

您可以做一些假设并转换为其中一种类型来进行数学运算。所以你可以有

Number c = a.intValue() + b.intValue();

你也可以变成这样

Integer c = a.intValue() + b.intValue();

如果您愿意承受一些舍入误差,或者

Float c = a.floatValue() + b.floatValue();

如果您怀疑自己不是在处理整数,并且可以解决可能的小精度问题。或者,如果您宁愿对性能造成小幅打击而不是出现该错误,

BigDecimal c = new BigDecimal(a.floatValue()).add(new BigDecimal(b.floatValue()));

【讨论】:

  • 你几乎是对的。但我不同意“......所以它知道它有 001110 但它不知道这是否意味着 9 或 14 或其他值。”。 Java 具有运行时类型信息。 Java 将知道 Number 对象的运行时类型是 Integer 还是 Double 或其他类型。从理论上讲,您可以开始区分类型并开始编程 public Number plus(Number a, Number b) { if (a instanceof Integer && b instanceof Integer) { return new Integer(a.intValue + b.intValue(); } .. .等等...
  • 为什么会有人将数字解析为整数而不是仅仅使用整数类型呢?
【解决方案2】:

也可以为您创建一种处理添加的方法。现在我不知道这会对性能造成什么影响,但我认为它会比使用 BigDecimal 小。

public static Number addNumbers(Number a, Number b) {
    if(a instanceof Double || b instanceof Double) {
        return a.doubleValue() + b.doubleValue();
    } else if(a instanceof Float || b instanceof Float) {
        return a.floatValue() + b.floatValue();
    } else if(a instanceof Long || b instanceof Long) {
        return a.longValue() + b.longValue();
    } else {
        return a.intValue() + b.intValue();
    }
}

【讨论】:

  • 您可以通过使用<N extends Number> 来避免重复自己,因此您只需检查N a 的类型。
  • @ndm13 在上面的 sn-p 我只是检查类型来告诉女巫要使用的方法。
  • 我的意思是不必同时检查 a 和 b 的类型。泛型将确保它们是相同的类型,因此您只需要检查即可。
  • 所以我同意使用泛型是一种更好的方法。但是您仍然需要检查 a 和 b 的类型。 Number number = addNumbers(new Double(1.275), new Long(14)); 只检查一个返回正确的 Double。 Number number = addNumbers(new Long(14), new Double(1.275)); 只检查一个返回不正确的 Long。
  • @ndm13 “在调用具有两个相同类型的方法时,您只需要检查一个类型”但这不是真的。这就是重点。您的签名允许以与原始类型完全相同的方式传入不同的类型。
【解决方案3】:

正确添加任意两种 java.lang.Number 的唯一方法是:

Number a = 2f; // Foat
Number b = 3d; // Double
Number c = new BigDecimal( a.toString() ).add( new BigDecimal( b.toString() ) );

这甚至适用于具有不同数字类型的两个参数。只要数字类型的 toString() 不会降低精度,它将(应该?)不会产生任何副作用,例如溢出或丢失精度。

【讨论】:

  • 如果我们可以让 Class> BigDecimal.getPrimitiveType() 返回 int.class 或 double.class 或其他什么
【解决方案4】:

java.lang.Number 只是所有原始类型包装类的超类(参见java doc)。根据您的目的使用适当的原始类型(doubleint 等)或相应的包装类(DoubleInteger 等)。

考虑一下:

Number a = 1.5; // Actually Java creates a double and boxes it into a Double object
Number b = 1; // Same here for int -> Integer boxed

// What should the result be? If Number would do implicit casts,
// it would behave different from what Java usually does.
Number c = a + b; 

// Now that works, and you know at first glance what that code does.
// Nice explicit casts like you usually use in Java.
// The result is of course again a double that is boxed into a Double object
Number d = a.doubleValue() + (double)b.intValue();

【讨论】:

  • 我明白这一点。但奇怪的是 Number 类本身并不直接支持它。
  • @amit.dev 并非所有子类都可以按照您期望的方式实现这一点。尤其是对于 Atomic 类型,很难为例如定义一个有用的合约。添加。而且Short.add(Long)也必然会出事。
  • @amit.dev: 为什么要直接支持?这是没有意义的,因为它会要求 java 支持奇怪的隐式强制转换或运算符重载,以便为每个要添加的类型排列。我在答案中添加了示例代码。也许这样会更清楚。
  • 注意:int 的包装类实际上是Integer
  • @Philip Number a = 1.5; 是双重的。 1.5f 是一个浮点数。
【解决方案5】:

使用以下内容:

Number c = a.intValue() + b.intValue(); // Number is an object and not a primitive data type.

或者:

int a = 2;
int b = 3;
int c = 2 + 3;

【讨论】:

    【解决方案6】:

    我认为你的问题有两个方面。

    为什么 Number 不支持 operator+?

    因为 Java 语言规范。没有指定这一点,也没有运算符重载。也没有一种编译时自然的方式将 Number 转换为某种基本类型,也没有为某种类型的操作定义自然添加。

    为什么 Number 不支持基本的算术运算?

    (复制自我的评论:)

    并非所有子类都能以您期望的方式实现这一点。尤其是对于 Atomic 类型,很难为例如定义一个有用的合约。添加。

    此外,如果您尝试将 Long 添加到 Short,则方法 add 会很麻烦。

    【讨论】:

      【解决方案7】:

      Number 是一个 abstract 类,您不能为其创建实例。如果你有一个正确的实例,你可以得到number.longValue()number.intValue() 并添加它们。

      【讨论】:

        【解决方案8】:

        首先,您应该知道 Number 是一个抽象类。这里发生的情况是,当您创建 2 和 3 时,它们被解释为原语并在这种情况下创建了一个子类型(我认为是一个整数)。因为 Integer 是 Number 的子类型,所以您可以将新创建​​的 Integer 分配给 Number 引用。

        但是,数字只是一种抽象。可以是整数,也可以是浮点数等,所以数学运算的语义会很模糊。

        Number 不提供经典的地图操作有两个原因:

        首先,Java 中的成员方法不能是运算符。它不是 C++。充其量,他们可以提供一个 add()

        其次,当您有两个输入(例如,浮点数除以整数)时,弄清楚要执行哪种类型的操作非常棘手。

        因此,您有责任将转换回您感兴趣的特定原始类型并应用数学运算符。

        【讨论】:

        • 错了,你不能创建抽象类的实例。
        • @incrediman:我在哪里表明我正在生成抽象类的实例?我明确指出创建了一个整数,然后将其分配给 Number 引用。
        • @incrediman,实际上,声明对抽象类的引用并将其指向现有对象是合法的;你只是不能声明一个抽象类的新实例。
        • @incrediman : 错了,你不能说 Uri 说的是,当他实际上从以下行开始时:...注意 Number 是一个抽象类... :) :) :)
        【解决方案9】:

        最好的答案是让 util 通过双重调度深入了解大多数已知类型(看看 Smalltalk addtition implementation)

        【讨论】:

          【解决方案10】:

          如果您知道一个数字的类型但不知道另一个,则可以执行类似的操作

          public Double add(Double value, Number increment) {
             return value + Double.parseDouble(increment.toString());
          }
          

          但它可能会很混乱,因此请注意潜在的准确性损失和NumberFormatExceptions

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-05-19
            • 1970-01-01
            • 2020-05-14
            • 2015-12-18
            • 2015-01-27
            • 1970-01-01
            相关资源
            最近更新 更多