Мне нужно инициализировать пару объектов разных типов от определений XML до bean-компонентов. У меня есть структура каталогов:
data \
|_ plant \
| |_ pine.xml
| |_ cucumber.xml
|_ animal \
| |_dog.xml
| |_cat.xml
|_ fungus \
...
Я хочу хранить его упорядоченно на двухуровневой карте: карта первого уровня содержит карты второго уровня. Каждая карта второго уровня содержит объекты одного типа (класса растений/животных/грибов...). Каждый класс расширяет класс Dict, который имеет некоторые общие поля и методы (name/getName()/setName(), desc/getDesc()/setDesc(), toString(), equals() и тому подобное.
Теперь, чтобы облегчить создание всех этих объектов, я хочу иметь фабричный метод Dict.fabricateAll(). И вот проблема: как сделать карты второго уровня, чтобы у них были точные параметры типа. Мне нужны не HashMap‹String, Dict›, а HashMap‹String, Plant›, HashMap‹String, Animal› и т. д.
На данный момент у меня есть следующий код:
public static Map<String, Map<String, ? extends Dict>> fabricateAll() {
File topDir = new File(Dict.defDir);
File[] subDirs = topDir.listFiles();
Map<String, Map<String, ? extends Dict>> dicts = new HashMap<>(subDirs.length);
String type;
String handle;
Map<String, ? extends Dict> dict;
for (File subDir : subDirs) {
type = subDir.getName();
File[] xmlFiles = subDir.listFiles(new FilenameFilter() {public boolean accept(File dir, String name) { return name.toLowerCase().endsWith(".xml"); }} );
dict = new HashMap<>(xmlFiles.length);
for (File defFile : xmlFiles) {
handle = defFile.getName();
handle = handle.substring(0, handle.lastIndexOf('.')-1);
try {
Class c = Class.forName(type);
Constructor bob = c.getConstructor(Class.forName("java.lang.String"));
dict.put(handle, bob.newInstance(handle));
} catch (Exception e) {
e.printStackTrace();
}
}
dicts.put(type, dict);
}
return dicts;
}
Но есть проблема со строкой: dict.put(handle, bob.newInstance(handle));
Сообщение об ошибке: "метод, указанный в интерфейсе Map‹K,V›, не может быть применен к заданным типам. Требуется: String, CAP#1 Найдено: String,Object
Да, этот newInstance() генерирует объекты и, вероятно, должно быть какое-то приведение, но к чему? Я не хочу здесь просто "(Dict)", я бы предпочел "(Растение)", "(Животное)"... в зависимости от ситуации. Как это сделать?
Map<String, Map<String, ? extends Dict>>
. Таким образом, клиентский код не может знать, содержат ли карты растения, кошек или что-то еще. Все, что он знает, это то, что они являются экземплярами Dict. Так закиньте его в Dict, и все будет хорошо. Тем не менее, я бы использовал для этого не отражение, а фабрики (одна фабрика на тип Dict), которые позволили бы делать то, что вы хотите. - person JB Nizet   schedule 24.02.2013Map<String, ? extends Dict>
, вы все равно не можете узнать, что содержит карта. Все, что вы можете сделать, это проверить, является ли значение экземпляром растения или животного (используяinstanceof
), и преобразовать значение в растение или животное. Итак, что вы получаете, делая картуMap<String, Animal>
, поскольку вы возвращаете ее какMap<String, ? extends Dict>
. Ваша фабричная идея действительно является решением. Вам понадобится метод для возврата Plant или Animal (в зависимости от класса), и вам потребуется, чтобы карта была типаMap<String, Plant>
или типаMap<String, Animal>
, в зависимости от каталога. - person JB Nizet   schedule 24.02.2013