我有以下情况:
我使用了command
模式,如下所示:
public interface Command<T extends EObject>
{
void runCommand(T classToMap, String fieldForMapping);
}
正如有人可能会注意到的那样,我正在使用Eclipse Modelling Framework
,但这对此问题无关紧要。
在另一个类中,我有一个HashMap作为mappingDirectory,该键返回另一个Map。
private final static HashMap<String, LinkedHashMap<String, Command>> mappingDirectory = new HashMap<String, LinkedHashMap<String, Command>>();
对于这个mappingDirectory,我添加了各种各样的地图:
private final static LinkedHashMap<String, Command> classOneMappings = new LinkedHashMap<String, Command>();
private final static LinkedHashMap<String, Command> classTwoMappings = new LinkedHashMap<String, Command>();
......等等。这些地图充满了键和提到的命令界面:
classOneMappings.put("someKey", new Command<ClassOne>()
{
@Override
public void runCommand(ClassOne classToMap, String fieldForMapping)
{
classToMap.setName(fieldForMapping);
};
});
最后,我有许多实现了不同类型的命令接口。那么如何才能避免仅使用Command
和mappingDirectory
以及包含的地图的原始类型?
我认为可以这样工作:
HashMap<String, LinkedHashMap<String, Command<? extends EObject>>> mappingDirectory = new HashMap<String, LinkedHashMap<String, Command<? extends EObject>>>();
并声明其他地图的特定类型全部 EObjects
:
private final static LinkedHashMap<String, Command<ClassOne>> classOneMappings = new LinkedHashMap<String, Command<ClassOne>>();
但遗憾的是将它们放入mappingDirectory
会导致编译错误。
也许我只是在忽视某些事情,所以任何帮助都会受到赞赏!
答案 0 :(得分:2)
最外层级通配符与嵌套通配符之间的区别在于,嵌套通配符不会捕获。
考虑以下2个声明:
List<? extends Number> list = new ArrayList<Integer>(); // 1
List<List<? extends Number>> list2 = new ArrayList<List<Integer>>(); // 2
List<List<? extends Number>> list3 = new ArrayList<List<? extends Number>>();// 3
虽然第一个声明完全有效,但我们知道List<? extends Number>
可以捕获ArrayList<Integer>
,但第二个声明无效。 List<List<? extends Number>>
,不会捕获ArrayList<List<Integer>>
。你必须只使用第三个声明。
因此,如果您已声明Map
,请将相同的逻辑应用于地图:
private static HashMap<String, LinkedHashMap<String, Command<? extends EObject>>> mappingDirectory =
new HashMap<String, LinkedHashMap<String, Command<? extends EObject>>>();
您只能添加Map
这样的内容:
private static LinkedHashMap<String, Command<? extends EObject>> classOneMappings =
new LinkedHashMap<String, Command<? extends EObject>>();
private static LinkedHashMap<String, Command<? extends EObject>> classTwoMappings =
new LinkedHashMap<String, Command<? extends EObject>>();
答案 1 :(得分:1)
注1:泛型是不变的!网上有很多文章讨论这个话题。
注意2:您在数据结构的两个“层”中使用泛型。这有时会令人困惑。
您的问题如下:您正在使用匿名类声明创建Command类型的子类型。但是,您将目录的内部映射声明中的值类型限制为仅限Command。通过以下变体,您还可以使用子类型:
Map<String, Map<String, ? extends Command<?>>> mappingDirectory = ...
现在你可以(并且应该)声明
Map<String, Command<ClassOne>> classOneMappings = ...
并将其放入目录:
mappingDirectory.put("...", classOneMappings);
(请注意,我只使用了接口类型Map。)
编辑:
对于以下示例,我将使用类Number
,Integer
和Double
(所有包java.lang
)。他们在这里做得很好。在实例化泛型时,我还将使用Java 7进行漂亮的类型推断。
我将从Command
界面开始:
interface Command<T extends Number> {
void run(T number, String field);
}
通过它,您可以执行以下操作:
class NestedGenerics {
private static final Map<String, Map<String, ? extends Command>> DIRECTORY = new HashMap<>();
public static void main(String[] args) {
Map<String, Command<Integer>> integerMap = new HashMap<>();
Map<String, Command<Double>> doubleMap = new HashMap<>();
integerMap.put("integer_command", new Command<Integer>() {
@Override
public void run(Integer number, String field) {
System.out.println(field + ": " + number);
}
});
doubleMap.put("double_command", new Command<Double>() {
@Override
public void run(Double number, String field) {
System.out.println(field + ": " + number);
}
});
DIRECTORY.put("integers", integerMap);
DIRECTORY.put("doubles", doubleMap);
DIRECTORY.get("integers").get("integer_command").run(Integer.valueOf(42), "integer field");
DIRECTORY.get("doubles").get("double_command").run(Double.valueOf(42.0), "double field");
}
}
这里的一个小缺点是,您在目录的声明中使用Command
类型作为原始类型。但是当您将各种Command
子类型的各种地图放入此目录时,绑定到Command
的{{1}}类(提醒:Number
)可能足够用于此目的。
这个例子编译并运行得很好。它的输出是:
整数字段:42
双场:42.0