以类为键的TreeMap

时间:2015-10-01 17:27:22

标签: java class generics interface treemap

我正在尝试为不同类的对象编写一种注册表。

我有以下内容:

public interface DbObject{
    void setId(long id);
    Long getId();
}

实现此接口的原型类如下:

public class BasicDbObject implements DbObject{
    private long id=null;
    void setId(long id){
        this.id=id;
    }
    Long getId(){
        return id;
    }
}

我想构建此接口的各种不同实现。 我希望能够有一个Map对象,它从每个实现类映射到实例Map。

这样的事情:

Map <Class<C implements DbObject> , Map<Long, C>> registry = new TreeMap/HashMap/SomeOtherKindOfMap (...)

我知道我可以做类似

的事情
Map <String,Map<Long,DbObjects>> registry = new ...

但是这样我就不得不写一些代码来确定名称,比较类等等。有没有更简单的方法来实现这一目标?

所以我想知道:是否可以将类对象作为树图中的键?

声明一个映射对象的语法是什么,它从实现类C映射到映射对象,每个映射对象从长对象(id)映射到C的实例?

我希望能够执行以下请求:

BasicObject bo = registry.get(BasicObject.class).get(42);

保证id

BasicObject bo=new BasicObject(...);
innerMap = new SomeMap<Long,BasicObject>();
innerMap.put(42,bo);
registry.put(BasicObject.class,innerMap);

之前。

请告诉我,如果仍然不清楚,我很难解释,因为英语不是我的母语。

提前谢谢。

编辑:

事实证明,在地图周围定义泛型类时,我可以做一些非常接近我想要的事情:

public class ObjectRegistry <T extends DbObject>{

    private HashMap<Class<T>, TreeMap<Long,T>> registry=null;

    ObjectRegistry(){
        registry=new HashMap<Class<T>, TreeMap<Long,T>>();
    }
    public void register(T dbObject){
        TreeMap<Long, T> map = registry.get(dbObject.getClass());
        if (map==null){
            map=new TreeMap<Long,T>();
            registry.put((Class<T>) dbObject.getClass(),map);
        }
        map.put(dbObject.getId(),dbObject);
    }

    public <T extends DbObject>T get(Class<T> objectClass,long id){
        TreeMap<Long, T> map = (TreeMap<Long, T>) registry.get(objectClass);
        if (map != null){
            return map.get(id);
        }
        return null;
    }

    public TreeMap<Long,T> getAll(Class<T> dbObjectClass) {
        return registry.get(dbObjectClass);
    }
}

我使用TreeMap作为内部映射,因为我想轻松返回按id排序的Class实例。

所以精致的问题是: 有没有办法在没有Class头中的<T extends DbObject>子句的情况下执行此操作?

编辑2:

再次思考,事实证明,约翰的回答恰恰是解决这个问题的方法。

这是我的最终代码:

HashMap<Class<? extends DbObject>, TreeMap<Long, ? extends DbObject>> registry = null;

public <T extends DbObject> T get(Class<T> clazz, long id) {
    TreeMap<Long, T> map = (TreeMap<Long, T>) registry.get(clazz);
    if (map != null) {
        return map.get(id);
    }
    return null;
}

public <T extends DbObject> void register(T dbObject) {
    TreeMap<Long, T> map = (TreeMap<Long, T>) registry.get(dbObject.getClass());
    if (map == null) {
        map = new TreeMap<Long, T>();
        registry.put((Class<T>) dbObject.getClass(), map);
    }
    map.put(dbObject.getId(), dbObject);
}


public <T extends DbObject> TreeMap<Long, T> getAll(Class<T> dbObjectClass) {
    return (TreeMap<Long, T>) registry.get(dbObjectClass);
}

它不需要类头中的<T extends DbObject>子句。

1 个答案:

答案 0 :(得分:3)

  

所以我想知道:是否可以将类对象作为树图中的键?

TreeMap取决于密钥空间的总排序,由具有自然顺序的密钥类型(通过实现Comparable)或单独的Comparator对象建立你提供。 Class没有自然顺序。可以想象你可以写一个合适的Comparator,但这对我来说似乎非常有用。

但为什么你特别需要一个TreeMap?您没有描述任何其他类型Map至少无法解决的要求。特别是,我几乎总是发现HashMap是更好的选择,而且我也没有看到任何理由不适合这一点。它当然可以将Class类型的对象作为键。

此外,如果您确实不需要任何特定的实现,那么您最好将声明类型简单地称为Map。通过这种方式,您实际上可以提供任何Map实施,甚至可以更改您提供的实施方案。

  

声明一个映射对象的语法是什么,它从实现类C映射到映射对象,每个映射对象从长对象(id)映射到C的实例?

您要求对每个值的类型的约束取决于关联键的类型,但是无法声明强制执行此类关系的类型。特定关键字或特定值是否适合Map是否仅取决于地图的类型,而不是彼此的关键字。类型。

您可以编写围绕访问地图的通用方法,以提供您想要的外观,但数据检索方法需要进行转换。例如:

Map<Class<? extends DbObject>, Map<Long, ? extends DbObject>> registry = /*...*/;

<T extends DbObject> Map<Long, T> getRegistryMap(Class<T> clazz) {
    return (Map<Long, T>) registry.get(clazz);
}

<T extends DbObject> T get(Class<T> clazz, Long id) {
    Map<Long, T> map = getRegistryMap(clazz);

    return (map == null) ? null : map.get(id);
}

<T extends DbObject> T put(Class<T> clazz, Long id, T obj) {
    Map<Long, T> map = getRegistryMap(clazz);

    if (map == null) {
        map = new HashMap<>();
        registry.put(clazz, map);
    }
    return map.put(id, obj);
}

已更新以添加:

  

所以精炼的问题是:有没有办法做到这一点,没有Class头中的<T extends DbObject>子句?

是的,我已经写过了。只是打一个普通的类声明。您不需要泛型类来使用泛型方法。实际上,这两者是正交的。泛型类的常规方法可以使用该类的类型参数。这并不能使它们成为通用方法。如果一个方法声明它自己的类型参数,那么它就是通用的,就像我上面所做的那样。您的get()方法也会这样做,并且必须了解您在方法签名中明确声明的类型参数<T>会影响类的同名类型参数:它是一个不同的T