Reputation: 13
public class GenMethodDemo{
public GenMethodDemo(){
Sum.<Integer,Integer,Integer>sum(1,2);
}
public static void main(String args[]){
new GenMethodDemo();
}
}
class Sum{
public static final <S extends Number,Z extends S,X extends S> S sum(Z v1,X v2){
System.out.printf("v1=%1$s,v2=%2$s%n",v1.getClass(),v2.getClass());
return v1+v2;
}
Error get:
error: bad operand types for binary operator '+'
return v1+v2;
first type: Z
second type: X
where Z,S,X are type-variables:
Z extends S declared in method <S,Z,X>sum(Z,X)
S extends Number declared in method <S,Z,X>sum(Z,X)
X extends S declared in method <S,Z,X>sum(Z,X)
1 error
Can't understand what i'm doing wrong? If i change S.Z.X with Integer - all works fine but why with generics code won't compile?
Refactored code to:
public class GenMethodDemo2{
public GenMethodDemo2(){
Sum.<Integer>sum(1,2);
}
public static void main(String args[]){
new GenMethodDemo2();
}
}
class Sum{
public static final <S extends Integer> S sum(S v1,S v2){
System.out.printf("v1=%1$s, v2=%2$s%n",v1.getClass(),v2.getClass());
return v1+v2;
}
}
error: incompatible types: int cannot be converted to S
return v1+v2;
where S is a type-variable:
S extends Integer declared in method <S>sum(S,S)
1 error
So, S supposed to be an Integer or any subclass of Integer class, in any way it definitely should be possible to + their values. What's wrong with this version?
S extends Integer
but int cannot be converted to S
, how it could be? Why there is no autoboxing?
Upvotes: 0
Views: 189
Reputation: 140319
The problem you're experiencing is because there is no +
operator defined for Number
, only specific subclasses of Number
. For example, +
is defined for Integer
, Double
etc, but not BigInteger
, BigDecimal
or any other non-standard implementation of Number
.
There is no good way to do generic addition. You end up having to provide a BinaryOperator<S>
, so your code looks like:
sum(1, 2, Integer::sum);
sum(1.0, 2.0, Double::sum);
which is more verbose than just:
1 + 2
1.0 + 2.0
The compiler requires +
to be defined for the compile-time types of v1
and v2
. It doesn't matter if they are Integer
(or whatever) at runtime: the decision as to whether to allow the +
is made by the compiler, because it has to be able to guarantee that the method is type-safe for any arguments.
The method above is compiled to this:
public static final Number sum(Number v1, Number v2){
System.out.printf("v1=%1$s,v2=%2$s%n",v1.getClass(),v2.getClass());
return v1+v2;
}
This is called type erasure.
If +
isn't defined for a Number
, this code isn't allowed.
Upvotes: 11
Reputation: 3618
As a general solution for all inbuilt Number
extensions:
public static Number sum(final Number a, final Number b) {
return new BigDecimal(a.toString()).add(new BigDecimal(b.toString()));
}
(Note: there's no guarantee that toString()
will give a String
which is parseable by BigDecimal
but it does for all the inbuilt JDK Number
extensions to the best of my knowledge.)
If you wanted to do something more clever, you could do some checks with instanceof
to find the types of the inputs and work from there, but I tried that once for implementing Comparable
between all Number
s and the performance wasn't any better than casting to BigDecimal
.
Upvotes: -1