确保通用通配符与Java匹配

时间:2014-04-14 13:23:31

标签: java generics

所以我理解以下内容:

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的前面,所以这是来自内存,但希望一般的想法是明确的。

2 个答案:

答案 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>());

关于我的问题latest comment by the OPupdate of the question itself

更新

  

我如何使这更通用。例如使用在类级别定义的泛型类型替换List。像public void put(Class clazz,T1 t1)......你如何确保T1可以处理通用参数?

嗯,您仍然可以使用其他通用参数和类型约束来执行此操作。您需要为您希望拥有的集合使用根基类或接口,因为我们需要强制执行适当的泛型。假设基类型是Collection<T>接口(VectorArrayList,许多其他集合实现此接口)。上面的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>());

这与kocko's suggested answer

相同