Here's my sample code:
public class MyList<T extends Number> {
private List<T> items;
public void func() {
items.add(Integer.valueOf(1));
}
}
I think I should be able to add integer to items, but compilation fails:
Required type: T
Provided: Integer
Anyone knows what's wrong here?
CodePudding user response:
Let us consider a more complete version of your example:
public class MyList<T extends Number> {
private List<T> items = new ArrayList<>();
public void func() {
items.add(Integer.valueOf(1));
}
}
Suppose for the sake of argument that the compiler says that is OK.
And now we will create an instance and call the func
method:
MyList<Double> myDoubles = new MyList<>();
myDoubles.func();
Here is what happens.
We create a
MyList
instance whereT
isDouble
. That's OK: theDouble
class implements theNumber
interface.The
items
has a notional type ofList<Double>
and we initialize it with anArrayList
. So we now have a list what should only containDouble
values.In the call to
func
we attempt to add anInteger
to theList<Double>
. That is wrong!
That is what the compilation error is saying with the Required type: T Provided: Integer
message.
To spell it out, the compiler expects a value whose type is the type that T
is going to be at runtime. But you have given it Integer
. While Integer
implements the Number
interface, it is not necessary the same as what T
will be at runtime. That is the root cause of your compilation error.
So what is the solution?
Well it depends on what the (actual) problem that this example is intended to solve. If you want item
to be able to hold any Number
, you should change
private List<T> items = new ArrayList<>();
to
private List<Number> items = new ArrayList<>();
and items.add(Integer.valueOf(1))
should work.
On the other hand, if you want to add 1
to items
as an instance of the runtime type of T
, that is much more difficult. The problem is that the code of MyList
(as written) does not and cannot know what that type is! So, you need to EITHER pass the T
instance representing 1
as a parameter to func
OR pass a Class<T>
parameter to func
or the constructor and use reflection to create the instance of that class to represent 1
.
But if you want something to auto-magically convert the Integer
to what ever the actual runtime type of T
is ... that is not possible.
CodePudding user response:
When you're using a so-called bounded type parameter <T extends Number>
means that type T
is restricted by an upper bound expected to be a Number
or one of its subtypes.
There are plenty of options if you would think about subtypes of Number
: BigDecimal
, AtomicLong
, etc. That mean that your List<T>
at runtime might appear to be a List<AtomicLong>
and since behavior of generic types is invariant we would not be able to add anything that is not of type AtomicLong
into such list (no String
s, no Integer
s, etc.).
Therefore, compiler would disallow to add an Integer
into a List<T>
, where T
can be anything that extends Number
(or the Number
itself), because it can't be sure that it's type-safe.