简体   繁体   中英

Class<? extends SomeSuperclass> is apparently not allowed. What makes sense instead?

This is an Android app, but presumably it happens the same in Java. I have a type LevelFactory from which I derive Level1Factory, Level2Factory, etc. I want to have an array of these classes so I can instantiate a level given the array index.

I can just have a Class[] and put them in that and then just cast them to LevelFactory when I need to use them, but I was wondering what the proper thing to do is.

This is obviously an error "Incompatible types":

new Class<LevelFactory>[] {Level1Factory.class,Level2Factory.class};

However, I was surprised to see that this is also an error "Generic array creation":

new Class<? extends LevelFactory>[] {Level1Factory.class,Level2Factory.class};

The following works, but it gives the "Unchecked assignment" warning when assigned to a variable:

new Class[] {Level1Factory.class,Level2Factory.class};

The last is the only option I can get to work. I just ignore the warning, but I would like to do it using generics if that's actually possible.

I would recommend you to read Item 25 "Prefer lists to arrays" of book "Effective Java". There Joshua Bloch writes:

Why is it illegal to create a generic array? Because it isn't typesafe. If it were legal, casts generated by the compiler in an otherwise correct program could fail at runtime with a ClassCastException. This would violate the fundamental guarantee provided by the generic type system.

UPD: Maybe with concrete example it would be more understandable.

First of all arrays are covariant which means that SuperClass[] can be cast to SubClass[] and vice versa. It also means that it's legal to cast AnyConcreteClass[] to, say, Object[].

So Let's assume that it's possible to have Set<Cat>[] (but it is NOT). If somebody cast this array to Object[] and then add there a set of Dog instances, Java couldn't guarantee anymore that our array contains only sets of Cat instances. Breaking type safety it breaks essence of generics. That is why it's illegal have generic array.

Set<Cat>[] cats = new Set<Cat>[]; // illegal
Object[] objects = cats;
objects[1] = new Set<Dog>();
cats[1].add(new Cat()); // Oops! TypeCastException

Honestly saying this example also was taken from Effective Java :)

Two questions:

  1. Do you really need an Array? Arrays don't work great with generics. So an ArrayList<LevelFactory> might be the better solution

  2. Do you really need the downcast to the special type (Level1Factory, Level2Factory)? If they have a common super method which is defined in LevelFactory (lets say Level getLevel() ) you should not need to downcast them. Just call getLevel() and you get the correct Level instance from your factory.

Another note because this seems to be a common pitfall:

new Class<? extends LevelFactory>

This is not a valid statement (it does not matter if its an array or not). <? extends T> <? extends T> is only valid for the type on the left side. It defines that the generic type of the created Object can be T or derived from T. For Collections this does not mean that they can store objects of T or derived from T (which can a Collections of T anyway).

  1. List<LevelFactory> list = new ArrayList<LevelFactory>() This means you can add objects of LevelFactory, Level1Factory and Level2Factory to list . When you want to receive objects from list they are of type LevelFactory .

  2. List<? extends LevelFactory> list = new ArrayList<LevelFactory>() List<? extends LevelFactory> list = new ArrayList<LevelFactory>() Means you can receive objects of LevelFactory from list . BUT you cannot add any object to list in a typesafe way because you don't know the exact generic type of list . That because you can also assign new ArrayList<Level1Factory>() to list . Which means that you can't even add LevelFactory objects to list because they don't implement Level1Factory .

In general <? extends Something> <? extends Something> on collections is not what you want in most cases.

You can't create array this way new SomeClass<Type>[10] but only this way new SomeClass[10] . Consider using ArrayList<SomeClass<Type>> instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM