This is NOT homework.
Is it possible to write a generic method, something like this:
<T extends Number> T plusOne(T num) {
return num + 1; // DOESN'T COMPILE! How to fix???
}
Short of using a bunch of instanceof
and casts, is this possible?
The following 3 methods compile:
Integer plusOne(Integer num) {
return num + 1;
}
Double plusOne(Double num) {
return num + 1;
}
Long plusOne(Long num) {
return num + 1;
}
Is it possible to write a generic version that bound T
to only Integer
, Double
, or Long
?
There is no satisfactory solution for this, since java.lang.Number
doesn't specify anything that would be useful to compute the successor of a Number
.
You'd have to do instanceof
checks for the numeric box types, and handle each case specially. Note also that you may get an instanceof Number
that's none of the numeric box types, e.g. BigInteger
, AtomicLong
, and potentially unknown subclasses of Number
(e.g. Rational
, etc).
Look is very deceiving, here. The 3 methods may look alike, but autoboxing/unboxing hides the fact that they're actually very different at the bytecode level:
Integer plusOne(Integer);
Code:
0: aload_1
1: invokevirtual #84; //int Integer.intValue()
4: iconst_1
5: iadd
6: invokestatic #20; //Integer Integer.valueOf(int)
9: areturn
Double plusOne(Double);
Code:
0: aload_1
1: invokevirtual #91; //double Double.doubleValue()
4: dconst_1
5: dadd
6: invokestatic #97; //Double Double.valueOf(double)
9: areturn
Long plusOne(Long);
Code:
0: aload_1
1: invokevirtual #102; //Long Long.longValue()
4: lconst_1
5: ladd
6: invokestatic #108; //Long Long.valueOf(long)
9: areturn
Not only are the 3 methods invoking different xxxValue()
and valueOf()
methods on different types, but the instruction to push the constant 1
to the stack is also different (iconst_1
, dconst_1
, and lconst_1
).
Even if it's possible to bind a generic type like <T=Integer|Long|Double>
, the 3 methods are not genericizable into one method since they contain very different instructions.
Not all of the subclasses of Number can be autounboxed. BigDecimal, for instance, can't be autounboxed. Therefore the "+" operator won't work for it.
Not the prettiest solution ever, but if you rely in the following properties of every known implementation of Number (in the JDK):
You can implement it using reflection and using Generics to avoid having to cast the result:
public class Test {
@SuppressWarnings("unchecked")
public static <T extends Number> T plusOne(T num) {
try {
Class<?> c = num.getClass();
Constructor<?> constr = c.getConstructor(String.class);
BigDecimal b = new BigDecimal(num.toString());
b = b.add(java.math.BigDecimal.ONE);
return (T) constr.newInstance(b.toString());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
System.out.println(plusOne(1));
System.out.println(plusOne(2.3));
System.out.println(plusOne(2.4E+120));
System.out.println(plusOne(2L));
System.out.println(plusOne(4.5f));
System.out.println(plusOne(new BigInteger("129481092470147019409174091790")));
System.out.println(plusOne(new BigDecimal("12948109247014701940917.4091790")));
}
}
The return is done using an apparently unsafe cast but given that you're using a constructor of the class of some T or child of T you can assure that it will always be a safe cast.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With