Re: Another generics question: List<Class<?>> ls = ?
On 2009-04-18 19:03:05 -0400, Mark Space <markspace@sbc.global.net> said:
Lew wrote:
For the same reason you can't do
...snip...
Given 'Sub' extends 'Parent', it is not true that 'Foo<Sub>' extends
'Foo<Parent>'. If it did, it would allow illegal actions.
This almost makes sense to me. What I was expecting was it to work like this:
List<?> lx = new ArrayList<String>();
lx.add( 0, "test" ); // error
String s = lx.get( 0 ); // warning, must cast
That last line is actually an error, not a warning, without the cast.
So assigning the types (the first line) is ok. That you can't put
anything into lx (second line) is fine, that's because you don't know
the actual type of lx. But you can at least get Object out. And you
can cast the Object to a String and hope you are correct.
So by analogy:
List<Class<String>> ls = new ArrayList<Class<String>>();
List<Class<?>> al1 = ls; // oops
al1.add( 0, String.class ); // works
Class<?> c = al1.get( 0 ); // works
I was expecting to work the same way. However, this time both the add
and get are ok, and it's the assignment of ls to al1 that fails. I
think I'm going to have to review my generics a bit, I've got something
mixed up.
It does work the same way: the relationship between generic type
arguments is decided by what would be assignable to and from what. The
following is perfectly valid Java:
List<Class<?>> lclass = new ArrayList<Class<?>> ();
lclass.add (String.class);
lclass.add (List.class);
However, it has implications for your example. If List<Class<String>>
were assignable to List<Class<?>>, you'd then be able to add List.class
to it. What you actually want is a list of some type that is assignable
to Class<?> but is not assignable to from Class<List>:
List<Class<String>> lStringClass = new ArrayList<Class<String>> ();
// This is fine.
lStringClass.add (String.class);
List<? extends Class<?>> lClass = lStringClass;
// Error as above.
lClass.add (String.class);
lClass.add (Object.class);
// Error, but can cast, as above.
Class<String> c = lClass.get(0);
This is a pretty hairy edge case, but I can see it coming up if you had
a Plugin interface and a list of its implementing classes and wanted to
do something with any list of classes:
public static void main(String[] args) {
List<Class<? extends Plugin>> pluginClasses = new
ArrayList<Class<? extends Plugin>>();
pluginClasses.add(SoundPlugin.class);
pluginClasses.add(ThunderPlugin.class);
ensureLoadable (pluginClasses);
}
private static void ensureLoadable(
List<? extends Class<?>> classes) {
for (Class<?> c : classes) {
System.out.println(c);
}
}
HTH,
-o