Re: Generics question
kelvSYC wrote:
Suppose I have a class Foo, which has subclasses Foo1 and Foo2. Now,
suppose I need a map whose key type is Class<T extends Foo> and whose
value type is Set<T> (that is, if the key type is Foo1.class, then the
value type is Set<Foo1>).
Is it even possible to declare such a type using generics or do I have
to do something in a roundabout way?
I think a map type you need is as follows:
Map<Class<? extends T>, Set<? extends T>>
However, using this type directly is not quite safe, for example:
Map<Class<? extends Foo>, Set<? extends Foo>> map
= new HashMap<Class<? extends Foo>, Set<? extends Foo>>();
// you can, of course, add some mappings...
map.put(Foo1.class, new HashSet<Foo1>());
map.put(Foo2.class, new HashSet<Foo2>());
// but, unfortunately, the following is also allowed...
map.put(Foo1.class, new HashSet<Foo2>());
map.put(Foo2.class, new HashSet<Foo1>());
// and you mast add unsafe cast on access...
Set<Foo1> set1 = (Set<Foo1>) map.get(Foo1.class);
Set<Foo2> set2 = (Set<Foo2>) map.get(Foo1.class); // <-- note a bug
So, it's good idea IMHO, to support your mapping strategy with a
specialized tool, for example:
class Mapper {
Map<Class<? extends Foo>, Set<? extends Foo>> map
= new HashMap<Class<? extends Foo>, Set<? extends Foo>>();
<X extends Foo> Set<X> put(Class<X> key, Set<X> value) {
return (Set<X>) map.put(key, value);
}
<X extends Foo> Set<X> get(Class<X> key) {
return (Set<X>) map.get(key);
}
};
Mapper mapper = new Mapper();
mapper.put(Foo1.class, new HashSet<Foo1>());
mapper.put(Foo2.class, new HashSet<Foo2>());
// the following are not allowed now...
mapper.put(Foo1.class, new HashSet<Foo2>()); // <-- compile error
mapper.put(Foo2.class, new HashSet<Foo1>()); // <-- compile error
// and access is also safe...
Set<Foo1> set1 = mapper.get(Foo1.class);
Set<Foo2> set2 = mapper.get(Foo1.class); // <-- compile error
The tool hides unsafe code, and, of course, may implement other methods
supporting your mapping.
piotr