所以我理解以下内容:
HashMap<Class<?>,List<?>> map
允许您插入任何配对。如何强制执行,以便您只能插入匹配的对。 e.g。
map.put(String.class, new Vector<String>());
map.put(Integer.class, new Vector<Integer>());
但不允许以下内容:
map.put(ClassA.class, new Vector<ClassB>()); //I want this to refuse to compile
这可能吗?
更新 感谢您的投入到目前为止。我理解抽象地图插入以在两个参数中强制执行公共类型。这将保持地图清洁,但我怎么能向编译器保证这是这种情况。
E.g。这会导致编译器发牢骚,并没有任何数量的转换似乎解决它(至少我已经尝试过)
List<String> list1 = map.get(String.class);
所以我现在正在使用以下内容,但我对它不太满意
List list2 = map.get(String.class);
NB我目前不在IDE的前面,所以这是来自内存,但希望一般的想法是明确的。
答案 0 :(得分:3)
您可以定义泛型类型(比如在封闭方法的范围内),并将其用作通用Class
和通用Vector
的类型(或者更好,通用{ {1}})。
Collection
您可以使用以下方式调用它:
public <T> void method(Class<T> clazz, Collection<T> collection) {
Map<Class<T>,Collection<T>> map = new HashMap<Class<T>, Collection<T>>();
map.put(clazz, collection); //this will compile
}
你可以注意到这个语句没有编译:
method(String.class, new ArrayList<String>());
//or
method(String.class, new Vector<String>());
答案 1 :(得分:1)
我不会直接使用HashMap
,因为它的默认方法不允许你想要的东西。我会根据HashMap
编写我自己的集合,它会公开它自己的put
方法。它可能看起来像这样:
public <T> void put(Class<T> clazz, List<T> list) {
internalMap.put(clazz, list);
}
假设internalMap
是此集合类的私有成员,且类型为HashMap<Class<?>, List<?>>
。注意通用参数的使用方法。你可以这样称呼:
myCollection.<Integer>put(Integer.class, new Vector<Integer>());
myCollection.<String>put(String.class, new Vector<String>());
甚至是这样:
myCollection.put(Integer.class, new Vector<Integer>());
myCollection.put(String.class, new Vector<String>());
然后这些将无法编译:
myCollection.<String>put(String.class, new Vector<Integer>());
myCollection.put(String.class, new Vector<Integer>());
更新:
我如何使这更通用。例如使用在类级别定义的泛型类型替换List。像public void put(Class clazz,T1 t1)......你如何确保T1可以处理通用参数?
嗯,您仍然可以使用其他通用参数和类型约束来执行此操作。您需要为您希望拥有的集合使用根基类或接口,因为我们需要强制执行适当的泛型。假设基类型是Collection<T>
接口(Vector
,ArrayList
,许多其他集合实现此接口)。上面的put
方法看起来像这样(我包含了一个带有internalMap
定义的虚构类:
public class ClassMap {
private Map<Class<?>, Collection<?>> internalMap
= new HashMap<Class<?>, Collection<?>>();
public <T, TCollection extends Collection<T>> void put(
Class<T> clazz,
TCollection collection) {
internalMap.put(clazz, collection);
}
public <T> Collection<T> get(Class<T> clazz) {
// Notice the cast, it is important as it will hide
// the "compiler grumble" you mention in your post
return (Collection<T>) internalMap.get(clazz);
}
}
对于具有显式泛型参数的案例,使用情况会略有更新:
myCollection.<Integer, Vector<Integer>>put(Integer.class, new Vector<Integer>());
myCollection.<String, ArrayList<String>>put(String.class, new ArrayList<String>());
和更短的版本:
myCollection.put(Integer.class, new Vector<Integer>());
myCollection.put(String.class, new ArrayList<String>());
由于泛型类型推断,也可以正常工作。
正如您必须注意到的,get
方法使用Collection<T>
接口。如果我们以与put方法相同的方式引入TCollection
泛型参数,那么现在必须始终提供泛型参数。因此,如果get方法定义如下:
public <T, TCollection extends Collection<T>> TCollection get(Class<T> clazz) {
return (TCollection) internalMap.get(clazz);
}
然后你有 2个问题:
ArrayList<String> list
= myCollection.<String, ArrayList<String>>get(String.class);
ArrayList<String>
(我认为编译时类型安全对您很重要),因此您将收到运行时如果你错了,也会出现无效演员表格。因此,get
方法根本不需要引入TCollection
泛型参数,它可以保证与Collection<T>
接口一起使用(因为所有值最终都会实现它)。如果该接口不适合您(您需要它的特定方法等),您可能要么明确地转换它(考虑到风险),要么使用更具体的接口作为根类型TCollection
的约束。例如,您可以使用Collection<T>
。List<T>
作为底线,关于TCollection
泛型参数的使用,可以省略它。将集合添加到地图后,您将不再知道所使用的确切实现。所以,上面的代码实际上与此相同:
public class ClassMap {
private Map<Class<?>, Collection<?>> internalMap
= new HashMap<Class<?>, Collection<?>>();
public <T> void put(Class<T> clazz, Collection<T> collection) {
internalMap.put(clazz, collection);
}
public <T> Collection<T> get(Class<T> clazz) {
return (Collection<T>) internalMap.get(clazz);
}
}
将会像这样使用:
myCollection.<Integer>put(Integer.class, new Vector<Integer>());
myCollection.<String>put(String.class, new ArrayList<String>());
或者那样:
myCollection.put(Integer.class, new Vector<Integer>());
myCollection.put(String.class, new Vector<String>());
相同