Home > Back-end >  Unable to add value to a java generic list
Unable to add value to a java generic list

Time:12-04

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.

  1. We create a MyList instance where T is Double. That's OK: the Double class implements the Number interface.

  2. The items has a notional type of List<Double> and we initialize it with an ArrayList. So we now have a list what should only contain Double values.

  3. In the call to func we attempt to add an Integer to the List<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 Strings, no Integers, 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.

  • Related