【问题标题】:Can I do arithmetic operations on the Number baseclass?我可以对 Number 基类进行算术运算吗?
【发布时间】:2010-10-06 13:56:39
【问题描述】:

我正在尝试在 Java 中创建一个通用类,它将对数字执行操作。在下面的例子中,添加如下:

public class Example <T extends Number> {

    public T add(T a, T b){
        return a + b;
    }

}

请原谅我的幼稚,因为我对 Java 泛型还比较陌生。此代码无法编译并出现错误:

对于参数类型 T、T,运算符 + 未定义

我认为添加“扩展编号”后代码会编译。是否可以使用 Java 或者我必须为每个 Number 类型创建覆盖的方法?

【问题讨论】:

  • 如果你想做这种事情,可能想看看 Scala 或 Groovy。它们具有运算符重载和特定领域的语言功能:)

标签: java


【解决方案1】:

数字没有与之关联的 + 运算符,也不能,因为没有运算符重载。

不过会很好。

基本上,您要求 java 自动装箱 Number 的后代,它恰好包括 Integer、Float 和 Double,可以自动装箱并应用加号运算符,但是,可能有任意数量的 Number 的其他未知后代不能自动装箱,直到运行时才能知道。 (该死的擦除)

【讨论】:

  • “可能有任意数量的 Number 的其他未知后代无法自动装箱”,例如 BigIntegerBigDecimal,它们都扩展了 Number
  • 我认为,如果 Number 有 .add(a,b) .sub(a,b) .mult(a,b) ...我们可以简单地使用这些代替,因此使用泛型,可能是一个由 IntegerHero 实现的接口 NumberHero 可以扩展类似于 Integer 的类(因为我们不能扩展最终类),可以做到吗?
【解决方案2】:

您的问题实际上与泛型无关,而是与运算符、基元与对象以及自动装箱有关。

想一想:

public static void main(String[] args) {
    Number a = new Integer(2);
    Number b = new Integer(3);
    Number c = a + b;
}

上面没有编译

public static void main(String[] args) {
    Integer  a = new Integer(2);
    Integer b = new Integer(3);
    Number c = a + b;
}

上面的确实编译,但只是因为自动装箱 - 这是一种在 Java 5 中引入的 hacky 语法胶水,并且仅适用于(在编译时)一些具体类型:int-Integer例如。

在幕后,Java 编译器正在重写最后一条语句(“我必须将 ab 拆箱以将 sum 运算符应用于原始数据类型,并将结果装箱以将其分配给对象 c”)因此:

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

Java 无法拆箱 Number,因为它在编译时不知道具体类型,因此无法猜测其原始对应物。

【讨论】:

    【解决方案3】:

    你可以这样做

        class Example <T extends Number> {
            public Number add(T a, T b){
                return new Double(a.doubleValue() + b.doubleValue());
            }
        }
    

    【讨论】:

    • 我不敢相信,即使在 2020 年,Java 也没有提供包含内置加/减/乘/除等操作的超类。我希望能够从一个类扩展并让 T 为 Integer、Double 等,并且数字运算开箱即用!
    【解决方案4】:

    是的,内森是正确的。如果你想要这样的东西,你必须自己写

    public class Example <T extends Number> {
        private final Calculator<T> calc;
        public Example(Calculator<T> calc) {
           this.calc = calc;
        } 
    
        public T add(T a, T b){
            return calc.add(a,b);
        }
    }
    
    public interface Calculator<T extends Number> {
        public T add(T a, T b);
    }
    
    public class IntCalc implements Calculator<Integer> {
        public final static IntCalc INSTANCE = new IntCalc();
        private IntCalc(){}
        public Integer add(Integer a, Integer b) { return a + b; }
    }
    
    ...
    
    Example<Integer> ex = new Example<Integer>(IntCalc.INSTANCE);
    System.out.println(ex.add(12,13));
    

    太糟糕了,Java 没有类型类(Haskell)或隐式对象(Scala),这个任务将是一个完美的用例......

    【讨论】:

      【解决方案5】:

      有类似的问题,答案是你不能那样做。

      您可以检查 a 和 b 是否是 Long/Double/Integer/etc 的实例。并将添加委托给如下方法:

      public Integer add(Integer a, Integer b) {
          return a+b; // this actually uses auto boxing and unboxing to int
      }
      

      而且您需要为每个扩展 Number 的类型创建一个,所以这并不可行。换句话说,不要将泛型用于数值运算。作为超类的数量非常有限。

      【讨论】:

        【解决方案6】:

        考虑Example&lt;Number&gt;+ 将如何解决这个问题?在Number 甚至没有add 或类似的Integer

        更糟糕的是考虑final class FunkyNumber extends Number { ... weird stuff, no add op ... }

        【讨论】:

          【解决方案7】:

          即使是 java 运行时库也有这个问题,大多数处理原语的方法都必须复制相同的功能。

          最快的选择是为一种类型编写代码,然后复制它并替换该类型以生成其他类型的方法。一个简短的脚本应该足以做到这一点。

          【讨论】:

            猜你喜欢
            • 2011-04-30
            • 2019-05-22
            • 1970-01-01
            • 2016-11-30
            • 2018-06-22
            • 2019-05-13
            • 2018-12-08
            • 2020-10-11
            • 2014-10-02
            相关资源
            最近更新 更多