You would need List<? super Class<? extends Entity>>
in order to statically “add” Classes of subtypes of Entity to the list.
But then you would not be able to statically “get” any Class of Entity from that list; you would get the static type “Object” back.
The original proposition of List<Class<? extends Entity>>
tells Java, that the list contains Class’es of things being a subtype (and all of the same subtype!) of Entity. But the compiler would not know, which exact subtype that would be.
The compiler also tells you that you could only add null
to the list, because it cannot statically validate that whatever Class of subtype of Entity you add to the list would be the same thing that is already in the list. Only null
can be safely cast to anything else.
Getting something from that list however would work, since the compiler knows that whatever is in the list is in any case a Class of at least Entity. And therefore, this is what you would “get”.
I aggree with @Abuse that the concept of parameterized types is very advanced.
And the horrible, horrible implementation of that concept in the form of “Java Generics” makes things even worse.
Just ignore generics and use the raw types.
You get compiler warnings about possible “heap pollution” (the “unchecked” warnings) every here and there, but just ignore them!
But hey, why are you not just using enums like @BurntPizza suggested.
And I would add that you even do not need/want reflection but a simple switch-case over all your enemy types with normal “new” instantiation would also do fine.
EDIT: Maybe a bit more about why List<? extends Enemy>
tells Java that this List would only contain instances of the exact same subtype of Enemy, as stated above.
Normally, we would think that this List can contain instances of any subtypes of Enemy and we wonder why the compiler then would not allow us to “add” some different enemies to it.
The problem probably is that we generally assume that List<? extends Enemy>
can contain any combination of instances of any subtypes of Enemy, because we are dealing with something called a “List” here and we are telling the List that it contains anything extending from Enemy.
But this thinking unfortunately leads into the wrong direction.
The fact is that type List<? extends Enemy>
tells Java that this “List” deals with instances of exactly one subtype of Enemy.
Which one, however, cannot be known. Hence the wildcard “?”.
The problem here is that the type system of Java unfortunately has no notion of lists, contrary to other languages that feature this built-in type, such as Haskell.
The java.util.List is just a normal parameterized interface.
It is just that we assume from the word “List” and from the fact that we can “add” to and “get” something from it, that the type parameter would apply to each element of the list individually. But this is not so.