简体   繁体   中英

Why can't I calculate with Number interface?

I wanted to make such class for calculating average value:

  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
    }
  }

I am not really getting why do we have interface Number if it doesn't abstract number . Because that's what interfaces do - abstract operations with data without holding the data themselves.

The above also makes it clear that you'll probably not be ever able to make your own number implementation (eg. number with unlimited size and precision for some really freaky experimental math programs).

I tried the same with Number instead of generic T with the very same results. Only difference is that Number x = 0 is actually valid.

Is there a way to trick java to compile this or do I have to be a "professional" java programmer and use doubles to calculate averages on byte arrays?

I am not really getting why do we have interface Number if it doesn't abstract number

interface Number does abstract the number for the purposes of storing and converting representations. It does not abstract the number for the purposes of making calculations, because the type of the result representation is not well defined.

The above also makes it clear that you'll probably not be ever able to make your own number implementation (eg. number with unlimited size and precision for some really freaky experimental math programs).

BigDecimal does it without any problem.

Number x = 0 is actually valid.

assigning Integer to Number is OK. Assigning Integer to something that extends Number (say, Double ) is not OK. That's why there is a difference.

Is there a way to trick java to compile this or do I have to go full retard professional Java programmer and use doubles to calculate averages on byte arrays?

You need to specify the desired representation of the result when you compute the average. You could abstract it out yourself and supply the "averager" ingerface, but the information about the desired representation needs to get into the method in one way or the other:

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);
}

Define several average makers, like this:

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; }
};

Now you can use them in your code together with the averageValue method:

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.

You are mixing up classes ( Number ) and primitive types ( int ). Operators like + or \\ are not supported for numeric classes ( + is supported for strings, but that's a special case; and unlike eg C# , Java does not support custom operator overloading).

A "number with unlimited size and precision for some really freaky experimental math programs" is actually supported with the BigDecimal class, which shows that (and how) you can indeed have your own Number implementation.

You could work with double s in the class. Since the average of Integers is a Double , this should be correct.

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;
    }
}

This compiles. Usage:

    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());

Output:

2.5
1.25

ERROR: incompatible types: int cannot be converted to T

T data = (T)(Integer)0;

ERROR: bad operand types for binary operator '+'

ERROR: incompatible types: int cannot be converted to T

in java doesn't exist operator overload Why doesn't Java need Operator Overloading?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM