3

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?

Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • `Number` is interface, not primitive type, so it doesn't support these operands. And you don't need to use doubles, just check what actual class is and perform operations on it. – Alex Salauyou Apr 29 '15 at 12:05
  • "Is there a way to trick java to compile" no, `Number` has no special privileges in Java so compiler reacts same way as you would try `Car+Car`. – Pshemo Apr 29 '15 at 12:06
  • possible duplicate of [Generic class for operations on Number](http://stackoverflow.com/questions/29940510/generic-class-for-operations-on-number) – Chetan Kinger Apr 29 '15 at 12:09

4 Answers4

4

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.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
1

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.

Marvin
  • 13,325
  • 3
  • 51
  • 57
  • Int is supposed to be Number and java 8 does implicitly cast primitive types to their class counterparts AFAIK. – Tomáš Zato Apr 29 '15 at 12:07
  • 1
    Actually `+` is supported with one VIP class which is String :) – Pshemo Apr 29 '15 at 12:08
  • 1
    @TomášZato It is automatically boxed/unboxed, but since T can be any numeric type like `Byte` or `Double` compiler can't decide which type should be used to generate code responsible for unboxing `T`. – Pshemo Apr 29 '15 at 12:10
  • @Pshemo: Indeed, my bad ;) I'll try to adjust my answer accordingly, just to be safe. – Marvin Apr 29 '15 at 12:11
1

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
  • 3
    "*You could work with doubles*"-- and face float-number issues even if all data are integer numbers. – Alex Salauyou Apr 29 '15 at 12:11
  • *If* you want to restrict yourself to a specific class, at least use `BigDecimal`. – Marvin Apr 29 '15 at 12:13
  • @SashaSalauyou True. But what use is a class that calculates the average of integers without using a double at some point? –  Apr 29 '15 at 12:14
  • @Marvin BigDecimal for whatß –  Apr 29 '15 at 12:14
  • @Tichodroma: `BigDecimal` instead of `double`, to be able to get correct results without float-number issues. – Marvin Apr 29 '15 at 12:16
  • @Tichodroma calculation of avg for long values using double will produce rough result – Alex Salauyou Apr 29 '15 at 12:16
  • @Marvin Feel free to edit my answer. But I think the question is not about the problems of floating point arithmetic but about `Number` and how it plays with Generics. That's what I've tried to answer. –  Apr 29 '15 at 12:18
0

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?

Community
  • 1
  • 1
Jose Ricardo Bustos M.
  • 8,016
  • 6
  • 40
  • 62