Reputation: 594
I have a simple generic list class. I'm trying to achieve this: given a list instance of which we know only that it contains instances of a specific subclass of a class, and given an instance of this class, add this instance to the list if it is instance of the contained (subclass) type, otherwise throw an exception (eg. ClassCastException). I tried the following:
class MyList<T>
{
private Class<T> genericClass;
private List<T> list = new ArrayList<>();
public MyList(Class<T> genericClass)
{
this.genericClass = genericClass;
}
public void add(T elem)
{
list.add(elem);
}
//...
public Class<T> getGenericParamClass()
{
return genericClass;
}
}
class A{}
class B extends A{}
class C extends A{}
class Program
{
public static void main(String... args)
{
MyList<B> list1 = new MyList<>(B.class);
MyList<C> list2 = new MyList<>(C.class);
MyList<? extends A> ls = checkStuff() ? list1 : list2;
ls.add(ls.getGenericParamClass().cast(lotsOfStuff())); //ERROR ?!!
}
static boolean checkStuff()
{
Random random = new Random();
return random.nextBoolean();
}
static A lotsOfStuff()
{
return new B();
}
}
I thought that given a Class object whose type parameter is the same as the type of a parameter of a method, I would be able to cast something using the former to be able to pass it to the latter. Alas, it seems I cannot: I get a compile-time error!
I could throw generics out the window, go full unchecked and just say:
A val = lotsOfStuff();
if (myList.getGenericParamClass().isInstance(val))
ls.add(val)
else
throw new SomeException();
But, that would probably create more problems than it would solve, and also it would really bug me.
Am I missing something here, or is it simply not possible the way I thought it out?
Edit: I understand fully well why something like this cannot work:
List<? extends Number> abc=new ArrayList<Integer>(); abc.add(new Integer(10));
But in my mind, the following transitivity holds: the type of the parameter of add() Is-The-Same-As the type parameter of MyList Is-The-Same-As the type parameter of the Class returned by getGenericParamClass() Is-The-Same-As the return type of the cast() method of that Class. I (as a human) can know that those unknown types are the same, because I am getting them from the same object.
Is there a fault in my logic, or is this a limitation of Java?
Upvotes: 0
Views: 135
Reputation: 70574
The compilation error is:
The method add(capture#2-of ? extends A) in the type MyList is not applicable for the arguments (capture#3-of ? extends A)
To understand that message, recall that ? extends A
stands for an unknown type that is a subtype of A
. That compiler can not know that the ? extends A
returned by lotsOfStuff()
is the same (or a subtype of) the ? extends A
that the MyList.add
method expects, and in fact, your program does not ensure that this is the case (because lotsOfStuff()
always returns a B
, even if it should be added to a list of C
.)
To express that the two are of the same type, we must use a type parameter. The easiest way to get one is to move the code doing the casting and throwing into class MyList<T>
(which already has a suitable type parameter), for instance by adding the following method:
void addOrThrow(Object o) {
add(genericClass.cast(o));
}
Upvotes: 2