【问题标题】:Why can't I calculate with Number interface?为什么我不能用 Number 接口计算?
【发布时间】:2015-04-29 12:02:23
【问题描述】:

我想做这样的类来计算平均值:

  public static class AverageValue<T extends Number> {
    T data = 0;  //ERROR: incompatible types: int cannot be converted to T
    int count = 0;
    public AverageValue() {}
    public AverageValue(T data) {
      this.data = data; 
      count = 1;
    }
    public void add(T num) {
      data+=num; //ERROR: bad operand types for binary operator '+'
      count++;
    }
    public T average() {
      return data/(T)count; //ERROR: incompatible types: int cannot be converted to T
    }
  }

我真的不明白为什么我们有接口 Number 如果它不抽象数字。因为这就是接口所做的 - 对数据进行抽象操作,而不是自己保存数据。

上面还清楚地表明,您可能永远无法实现自己的数字实现(例如,对于一些非常怪异的实验性数学程序,具有无限大小和精度的数字)。

我用Number 代替了通用T 进行了同样的尝试,结果完全相同。唯一不同的是Number x = 0实际上是有效的。

有没有办法欺骗 java 来编译它,还是我必须是一个“专业”的 java 程序员并使用双精度来计算字节数组的平均值?

【问题讨论】:

  • Number 是接口,不是原始类型,所以不支持这些操作数。而且您不需要使用双打,只需检查实际类是什么并对其执行操作即可。
  • "有没有办法欺骗 java 进行编译" 不,Number 在 Java 中没有特权,所以编译器的反应方式与您尝试 Car+Car 的方式相同。

标签: java inheritance interface


【解决方案1】:

我真的不明白为什么我们有接口 Number 如果它没有抽象数字

interface Number 确实出于存储和转换表示的目的抽象了数字。它不会为了进行计算而抽象出数字,因为结果表示的类型没有很好地定义。

上面还清楚地表明,您可能永远无法实现自己的数字实现(例如,对于一些非常怪异的实验性数学程序,具有无限大小和精度的数字)。

BigDecimal 没有任何问题。

Number x = 0 实际上是有效的。

Integer 分配给Number 是可以的。将Integer 分配给扩展Number(比如Double)的东西是不行的。这就是有区别的原因。

有没有办法欺骗 java 来编译它,还是我必须完全延迟专业的 Java 程序员并使用双精度来计算字节数组的平均值?

您需要在计算平均值时指定所需的结果表示形式。您可以自己将其抽象出来并提供“平均”接口,但有关所需表示的信息需要以一种或另一种方式进入方法:

interface AverageMaker<T extends Number> {
    T initialResult();
    T add(T a, Number b);
    T divideByCount(T a, int b);
}

public static <T extends Number, R extends Number> R averageValue(Iterable<T> items, AverageMaker<R> maker) {
    R res = maker.initialResult();
    int count = 0;
    for (T val : items) {
        res = maker.add(res, val);
        count++;
    }
    return maker.divideByCount(res, count);
}

定义几个平均制造商,像这样:

static final AverageMaker<Double> doubleAvg = new AverageMaker<Double>() {
    public Double initialResult() { return 0.0; }
    public Double add(Double a, Number b) { return a + b.doubleValue(); }
    public Double divideByCount(Double a, int b) { return a/b; }
};
static final AverageMaker<Integer> intAvg = new AverageMaker<Integer>() {
    public Integer initialResult() { return 0; }
    public Integer add(Integer a, Number b) { return a + b.intValue(); }
    public Integer divideByCount(Integer a, int b) { return a/b; }
};

现在您可以在代码中将它们与averageValue 方法一起使用:

List<Integer> a = new ArrayList<Integer>();
a.add(4);
a.add(8);
a.add(91);
a.add(18);
double avgDouble = averageValue(a, doubleAvg);
int avgInt = averageValue(a, intAvg);
System.out.println(avgDouble); // Prints 30.25
System.out.println(avgInt);    // Prints 30

Demo.

【讨论】:

    【解决方案2】:

    您可以在课堂上使用doubles。由于Integers 的平均值是Double,这应该是正确的。

    public class AverageValue<T extends Number> {
    
        double data = 0;
        int count = 0;
    
        public AverageValue() {
        }
    
        public AverageValue(T data) {
            this.data = data.doubleValue();
            count = 1;
        }
    
        public void add(T num) {
            data += num.doubleValue();
            count++;
        }
    
        public double average() {
            return data / count;
        }
    }
    

    这编译。用法:

        AverageValue<Integer> avgInt = new AverageValue<>();
        avgInt.add(1);
        avgInt.add(2);
        avgInt.add(3);
        avgInt.add(4);
        System.out.println(avgInt.average());
    
        AverageValue<Double> avgDouble = new AverageValue<>();
        avgDouble.add(1.1);
        avgDouble.add(1.2);
        avgDouble.add(1.3);
        avgDouble.add(1.4);
        System.out.println(avgDouble.average());
    

    输出:

    2.5
    1.25
    

    【讨论】:

    • "你可以使用双精度数"——即使所有数据都是整数,也会面临浮点数问题。
    • 如果你想把自己限制在一个特定的类,至少使用BigDecimal
    • @SashaSalauyou 真的。但是在某些时候不使用双精度数来计算整数平均值的类有什么用呢?
    • @Marvin BigDecimal for whatß
    • @Tichodroma: BigDecimal 而不是 double,以便能够在没有浮点数问题的情况下获得正确的结果。
    【解决方案3】:

    您正在混淆类 (Number) 和原始类型 (int)。数字类不支持+\ 等运算符(字符串支持+,但这是一种特殊情况;与C# 不同,Java 不支持自定义运算符重载)。

    BigDecimal 类实际上支持“对于一些非常奇怪的实验数学程序具有无限大小和精度的数字”,这表明(以及如何)您确实可以拥有自己的 Number 实现。

    【讨论】:

    • Int 应该是 Number 并且 java 8 确实将原始类型隐式转换为它们的类对应物 AFAIK。
    • 其实+支持一个VIP类,即String :)
    • @TomášZato 它是自动装箱/拆箱的,但由于 T 可以是任何数字类型,如 ByteDouble 编译器无法决定应该使用哪种类型来生成负责拆箱@的代码987654332@.
    • @Pshemo:确实,我的错;)为了安全起见,我会尝试相应地调整我的答案。
    【解决方案4】:

    错误:不兼容的类型:int 无法转换为 T

    T data = (T)(Integer)0;
    

    错误:二元运算符“+”的操作数类型错误

    错误:不兼容的类型:int 无法转换为 T

    在java中不存在运算符重载Why doesn't Java need Operator Overloading?

    【讨论】:

      猜你喜欢
      • 2020-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-03
      • 1970-01-01
      • 2018-07-29
      • 2022-01-03
      相关资源
      最近更新 更多