Im having the following situation:
I used the command
pattern like the following:
public interface Command<T extends EObject>
{
void runCommand(T classToMap, String fieldForMapping);
}
As someone might notice, I am working with the Eclipse Modelling Framework
but that doesnt matter for this question.
In another class I have a HashMap as a mappingDirectory, the key returns another Map.
private final static HashMap<String, LinkedHashMap<String, Command>> mappingDirectory = new HashMap<String, LinkedHashMap<String, Command>>();
To this mappingDirectory I add various maps looking like this:
private final static LinkedHashMap<String, Command> classOneMappings = new LinkedHashMap<String, Command>();
private final static LinkedHashMap<String, Command> classTwoMappings = new LinkedHashMap<String, Command>();
... and so on. These maps get filled with keys and the mentioned command interface:
classOneMappings.put("someKey", new Command<ClassOne>()
{
@Override
public void runCommand(ClassOne classToMap, String fieldForMapping)
{
classToMap.setName(fieldForMapping);
};
});
In the end I've got many implemented command interfaces with different types. So how can I avoid the raw type of just using Command
for the mappingDirectory
and the contained maps?
I thought it would be possible to get this working like this:
HashMap<String, LinkedHashMap<String, Command<? extends EObject>>> mappingDirectory = new HashMap<String, LinkedHashMap<String, Command<? extends EObject>>>();
and declaring the other maps with their specific types which are all EObjects
:
private final static LinkedHashMap<String, Command<ClassOne>> classOneMappings = new LinkedHashMap<String, Command<ClassOne>>();
but sadly putting them into the mappingDirectory
will result into a compilation error.
Maybe I am just overlooking something, so any help would be appreciated!
Note 1: Generics are invariant! There are lots of articles in the web that discuss that topic.
Note 2: You are using generics in two "layers" of your data structure. That will sometimes be confusing.
Your problem is the following: You are creating subtypes of the Command type with your anonymous class declarations. But you limit the value type in the inner map declaration of your directory to Command only. With the following variation you also allow subtypes:
Map<String, Map<String, ? extends Command<?>>> mappingDirectory = ...
Now you can (and should) declare
Map<String, Command<ClassOne>> classOneMappings = ...
and put it into the directory:
mappingDirectory.put("...", classOneMappings);
(Note that I used only the interface type Map.)
EDIT:
For the following example, I will use the classes Number
, Integer
, and Double
(all in package java.lang
). They are doing their job just fine here. I will also use Java 7 to have the nice type inference when instantiating generics.
I'll start with the Command
interface:
interface Command<T extends Number> {
void run(T number, String field);
}
With that you are able to do the following:
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");
}
}
A little disadvantage here is, that you use the Command
type as a raw type in the directory's declaration. But as you are putting various maps with various Command
subtypes into this directory, the Command
class' bound to Number
(remind: T extends Number
) might be enough for the usage.
This example compiles and runs just fine. Its output is:
integer field: 42
double field: 42.0