views:

244

answers:

6

I'm always struggling with generics. I don't know why this makes my go crazy. I have an old problem I've got several times.

class Father {
    ...
}

class Child1 extends Father {
    ...
}

class Child2 extends Father {
    ...
}

public class TestClass {
    private static Class<? extends Father>[] myNiceClasses = new Class<? extends Father>[] {
        Child1.class,
        Child2.class
    }
}

That does not work. The compiler complains like this:

Cannot create a generic array of Class<? extends Father>

If I change the (faulty) array line to

private static Class<Father>[] myNiceClasses = new Class<Father>[] {

the same error message occurs, but also this message is added:

Type mismatch: cannot convert from Class<Child1> to Class<Father>

The only working version is this line:

private static Class<?>[] myNiceClasses = new Class<?>[] {

This solution is not satisfying, because you cannot cast so easily.

So my question is: How to solve this problem? Where's the knot in my brain?

+2  A: 

See this thread at Sun forum

quant_dev
+3  A: 

What quant_dev's linked thread is saying is that generic arrays cannot be created because they cannot guarantee type safety. You can end up with things in the array the generics don't allow. The cleanest way I know of to do it anyway is:

private static Class<? extends Father>[] myNiceClasses =
        (Class<? extends Father>[]) new Class<?>[] {};
Zarkonnen
+8  A: 

This is because java does not support generic array creation. Generics work best with Collections. So you can use ArrayList to solve your problem:

        ArrayList<Class<? extends Father>> myNiceClasses = new ArrayList<Class<? extends Father>>();
  myNiceClasses.add(Child1.class);
  myNiceClasses.add(Child2.class);
+1. Use generics with Collections, not arrays.
seanmonstar
+1  A: 

Generics and arrays are a combination best avoided. The fact that the compiler doesn't like you to create a generic array is an artifact of the fact that Java Generics are a bolt-on addition to the language, and occasionally you can see the join. Another example is Exceptions, you can't combine those with generics either.

I would suggest avoiding arrays in situations like this. Use a generic List instead.

skaffman
+1  A: 

This is due to something called type erasure. The Java compiler erases all generic information during the compile process. (This is because generics was a late addition to the language, and Sun decided not to modify the JVM, for backwards compatibility. This means that the language knows about generics, but the JVM doesn't.)

Since the JVM doesn't know the generic type at runtime, it cannot instantiate an array of that generic type. In your case, the best it can do is instantiate an array of the raw type Class.

In instances like this, it's much better to use the collections framework, which fully supports generics.

To summarize: generics and arrays don't mix.

jqno
A: 

Alternatively, as amit.dev said, you may want to use a list. In which case, assuming the myNiceClasses field is supposed to be a constant, you can use Collections.unmodifiableList to make sure the list can't be changed:

private static final List<? extends Father> myNiceClasses;
static {
    ArrayList<? extends Father> l = new ArrayList<? extends Father>();
    l.add(Child1.class);
    l.add(Child2.class);
    myNiceClasses = Collections.unmodifiableList(l);
}

It's fairly verbose but has the advantage of being entirely constant and hence easier to reason about.

Zarkonnen