Tomáš Zato
Tomáš Zato

Reputation: 53129

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?

Upvotes: 3

Views: 3022

Answers (4)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726569

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.

Upvotes: 4

Jose Ricardo Bustos M.
Jose Ricardo Bustos M.

Reputation: 8164

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?

Upvotes: 0

Marvin
Marvin

Reputation: 14255

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 e.g. 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.

Upvotes: 1

user1907906
user1907906

Reputation:

You could work with doubles 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

Upvotes: 1

Related Questions