Java Reference
In-Depth Information
There are some obvious problems with the method signature. The parameters
n1
and
n2
could be of any
parameterized type of
Wrapper<T>
class. For example, the following call would be a valid call for the
sum()
method:
// Try adding an Integer and a String
sum(new Wrapper<Integer>(new Integer(125)), new Wrapper<String>("Hello"));
Computing the sum of an
Integer
and a
String
does not make sense. However, the code will compile and you
should be ready to get some runtime exceptions depending on the implementation of the
sum()
method. You must
restrict this kind of code from compiling. It should accept two
Wrapper
objects of type
Number
or its subclasses, not just
anything. Therefore, you do know the upper bound of the type of the actual parameter that the
Wrapper
object should
have. The upper bound is the
Number
type. If you pass any other type, which is a subclass of the
Number
type, it is fine.
However, anything that is not a
Number
type or its subclass type should be rejected at compile time. You express the
upper bound of a wildcard as
<? extends T>
Here,
T
is a type.
<? extends T>
means anything that is of type
T
or its subclass is acceptable. Using your upper
bound as
Number
, you can define your method as
public static double sum(Wrapper<? extends Number> n1, Wrapper<? extends Number> n2){
Number num1 = n1.get();
Number num2 = n2.get();
double sum = num1.doubleValue() + num2.doubleValue();
return sum;
}
The following snippet of code inside the method compiles fine:
Number num1 = n1.get();
Number num2 = n2.get();
No matter what you pass for
n1
and
n2
, they will always be assignment-compatible with
Number
because the
compiler will make sure that the parameters passed to the
sum()
method follow the rules specified in its declaration of
<? extends Number>
. The attempt to compute the sum of an
Integer
and a
String
will be rejected by the compiler.
Consider the following snippet of code:
Wrapper<Integer> intWrapper = new Wrapper<Integer>(new Integer(10));
Wrapper<? extends Number> numberWrapper = intWrapper; // Ok
numberWrapper.set(new Integer(1220)); // A compile-time error
numberWrapper.set(new Double(12.20)); // A compile-time error
Can you figure out the problem with this snippet of code? The type of
numberWrapper
is
<? extends Number>
,
which means it can refer to (or it is assignment-compatible with) anything that is a subtype of the
Number
class. Since
Integer
is a subclass of
Number
, the assignment of
intWrapper
to
numberWrapper
is allowed. When you try to use the
set()
method on
numberWrapper
, the compiler starts complaining because it cannot make sure at compile time that
numberWrapper
is a type of
Integer
or
Double
, which are subtypes of a
Number
. Be careful with this kind of compiler
error when working with generics. On the surface, it might look obvious to you and you would think that code should
compile and run fine. Unless the compiler makes sure that the operation is type-safe, it will not allow you to proceed.
After all, compile-time and runtime type-safety is the primary goal of generics!