tags:

views:

183

answers:

5

I want to check if a given Class is assignable to a java.util.Collection, and if so create a new instance of it.

I tried the following:

Class<?> clazz = ... // I got this from somewhere

if (!clazz.isInterface() && java.util.Collection.class.isAssignableFrom(clazz)) {
    java.util.Collection<?> collection = clazz.newInstance();
}

Predictably it doesn't work, since it cannot convert to an unknown type to a java.util.Collection. I thought of adding a cast but that seems like a hack.

I also thought of doing this:

Class<? extends java.util.Collection<?>> collectionClass = Class<? extends java.util.Collection<?>> clazz;
java.util.Collection<?> collection = clazz.newInstance();

Now there's no need for the cast at newInstance but I still have to cast the Class object.

What's the right way to do this? Thanks.

(for clarity I removed the try/catch around newInstance in case I'm trying to instantiate an abstract class)

A: 

You have to use a cast when you are using newInstance() there is no way around that. Your first example is fine.

Francis Upton
In some cases no cast is needed. Touch this, Class<String> clazz= String.class; String str = clazz.newInstance();
Adeel Ansari
@Vinegar - however, I think you will find that there is an implicit typecast here.
Stephen C
Yes indeed, but as you see I am not doing anything myself. And that is a real concern. Implicit type cast are ubiquitous.
Adeel Ansari
+2  A: 

Cast is a must because of the wild card you are using. i.e. Class<?> clazz. Casting is not a hack, c'mon. And here its reasonably fine, as its under the check.

Adeel Ansari
+1 Agreed - that's the whole point of casting, to tell the compiler that you've checked and now know that a variable is of a more specific type than its existing declaration. Since you've checked the class is assignable as a Collection, casting the `clazz` in your code snippet is exactly what I'd do.
Andrzej Doyle
A: 

This is an interesting question. The second form avoids the need for an explicit type cast on the result of calling clazz.newInstance(). In reality, the emitted code will include an implicit type cast because the compiler doesn't know whether the newInstance() method cheats (does an "unsafe conversion") and returns something of the wrong type.

Stephen C
A: 

Try clazz.asSubClass(Collection.class).newInstance(), this should give an already typed Collection<?> instance

Willi
A: 

After the if statement, you know that clazz is a subclass of collection (even though the compilr does not know that). So you can cast it:

Class<?> clazz = ... // I got this from somewhere

if (!clazz.isInterface() && java.util.Collection.class.isAssignableFrom(clazz)) {
    Class<Collection<?>> collectionClazz = (Class<Collection<?>>)  clazz;
    java.util.Collection<?> collection = collectionClazz.newInstance();
}

The compiler will complain that the cast is not checked at runtime (because at runtime the generic parameters go missing), but that's ok.

... just saw the "asSubclass" method. Does the same job, but it's checked at runtime - it redoes the test that your if() does.

Having said that - whenever I see the instanceof keyword these days I instantly suspect that there should be a generic type being used. That is, the class or method containing the code above should probably have access to a parameterised class T and be using Class.