Reputation: 53129
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
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
retardprofessional 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
Upvotes: 4
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
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
Reputation:
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
Upvotes: 1