I want to create a kind of in-memory database into which I can put objects of any type that extends EntityProxy
. I want to index every object according to its type and id number (which every EntityProxy
has - something simple like creating a HashMap for each type.
I could implement this开发者_运维百科 manually with something like
public void put(Object o)
{
if (o instanceof Car)
carIndex.put(o.getId(), o);
else if (o instanceof Bus)
busIndex.put(o.getId(), o);
...
}
But of course I don't want to. I want something more like
public void <T extends EntityProxy> put(T o)
{
indexMap.get(o.class).put(o.getId(), o);
}
I just don't quit have the generics vocabulary to do this. I don't know what the question mark means in template definitions, really - will something like
HashMap<Class<?>, HashMap<Long, EntityProxy>> indexMap
work?
That map is OK; if you really need to add a tiny bit of constraint, just try:
Map<Class<? extends EntityProxy>, HashMap<Long, EntityProxy>> indexMap;
This would make sure the key class can only be an EntityProxy.class or subclass.
You can think of the question mark as some "anything", but anonymous. So <?>
means really anything -- any Object
, <? extends EntityProxy>
means anything that fulfils this condition (passes the "instanceof EntityProxy
" test).
The type safety here is less than desired, as you can still put anything as key and anything in that map. I mean, you can legally put this in the map:
indexMap.put(EntityProxy1.class, new HashMap<Long, EntityProxy2>());
(assuming EntityProxy1
and EntityProxy2
are both subclasses of EntityProxy
) since there's no correlation between the key and the value. To enforce that, the put()
method of the map would need to be declared like this:
<T extends EntityProxy> put(Class<T> key, HashMap<Long, T> value);
T
is pretty much like ?
but the main difference is that it provides you with a name that you can refer to it in that context.
So, if I said that ?
stands for "anything", I would say T
stands for "something", as you can refer to that something once you declare it.
But you would need a custom data structure for this, as java.util.Map
does not provide this kind of constraint. If you're using it as shown in your code sample, I don't think you really need these enforcements.
Note that in this example I used List. You could easily replace this with your Collection
class of choice and adapt accordingly.
public interface Classifiable {
String classification();
}
public abstract class Automobile implements Classifiable {
// String classification defined in child classes
}
public class Car extends Automobile {
public String classification() { return "Car"; }
}
public class Bus extends Automobile {
public String classification() { return "Bus"; }
}
public class AutoMap {
Map<String,List<Automobile>> theMap = new Map<String,List<Automobile>>();
public AutoMap() { }
public void add(Automobile a) {
String type = a.classification();
List<Automobile> list = theMap.get(type);
if(list == null) {
list = new LinkedList<Automobile>();
theMap.put(type,list);
}
list.add(a);
}
public List<Automobile> getAutosOfType(String type){
return theMap.get(type);
}
}
public static void main(String[] args) {
List<Automobile> autos = getRandomAutos(); // defined somewhere? :)
AutoMap theAutomap = new AutoMap();
}
If you don't mind using the class name, it's as simple as:
public void <T extends EntityProxy> put(T o)
{
HashMap map = indexMap.get(o.getClass().getName());
if (map == null)
{
map = new HashMap();
indexMap.put(o.getClass().getName(), map);
}
map.put(o.getId(), o);
}
This code will create the required sub-hashmaps as you go along.
If you use getClass().getName()
you get names on the form com.mypackage.Bus
. If you're willing to handle name collisions and only want the simple name (in this case "Bus"), use getClass().getSimpleName()
instead.
Try:
Map<Class<?>, Map<Long, EntityProxy>> indexMap = new HashMap<Class<?>, Map<Long, EntityProxy>>();
and
public void put(EntityProxy entityProxy){ // generics don't add anything here
...
(I didn't test it)
精彩评论