开发者

Class magic - sorting objects by type automatically

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)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜