以 类 为键的 TreeMap

TreeMap with Classes as Key

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

我有以下内容:

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

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

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

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

像这样:

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

我知道我可以做类似的事情

Map <String,Map<Long,DbObjects>> registry = new ...

但是这样我就不得不写更多的代码来确定名字,比较classes等等。有没有更简单的方法来完成这个?

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

声明映射对象的语法是什么,它从实现 classes 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);

之前。

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

提前致谢。


编辑:

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

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 进行内部映射,因为我想轻松地 return Class 按 id 排序的实例。

所以提炼后的问题是: 如果 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);
}

不需要 Class 头部的 <T extends DbObject> 子句。

So what I want to know: is it possible to have class objects as keys in a tree map?

TreeMap 取决于键 space 上的总顺序,由具有自然顺序的键类型(通过实现 Comparable)或单独的 Comparator 您提供的对象。 Classes 没有自然顺序。可以想象你可以写一个合适的Comparator,但我觉得那很做作。

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

此外,如果您确实不需要任何特定的实现,那么您最好将类型简单地声明Map。这样一来,您实际上可以 提供 任何 Map 实现,甚至可以在您发现这样做的理由时更改您提供的实现。

What would be the syntax to declare a map object, that maps from implementing classes C to a map objects each mapping from a long object (the id) to instances of 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);
}

更新补充:

So the refined question is: Is there a way to do this, without the <T extends DbObject> clause in the Class head?

是的,我已经写过了。只需在其周围添加一个简单的 class 声明即可。您不需要泛型 class 就可以拥有泛型方法。事实上,两者是正交的。 通用class的常规方法可以使用class的类型参数。这不会使它们成为通用方法。如果一个方法声明 它自己的 类型参数,那么它就是通用的,就像我上面做的那样。您的 get() 方法也这样做,重要的是要了解您在方法签名中显式声明的类型参数 <T> 隐藏了 class 的同名类型参数:它是不同的 T.