Мне нужно инициализировать несколько объектов od разных типов из определений XML в beans. У меня есть структура каталогов:Карта карт разных типов
data \
|_ plant \
| |_ pine.xml
| |_ cucumber.xml
|_ animal \
| |_dog.xml
| |_cat.xml
|_ fungus \
...
Я хочу сохранить его упорядоченным в двухуровневой карте: карта первого уровня хранит карты второго уровня. Каждая карта второго уровня содержит объекты одного типа (класса растений/животных/грибов ...). Каждый класс расширяет класс Dict, который имеет некоторые общие поля и методы (имя/getName()/setName(), desc/getDesc()/setDesc(), toString(), equals() и т. Д.
, чтобы облегчить создание всех этих объектов, я хочу иметь фабричный метод Dict.fabricateAll(). И вот в чем проблема: как создать карты второго уровня, чтобы они имели точные параметры типа. Я не хочу, чтобы HashMap < String, Dict > но HashMap < String, завод >, HashMap < String, животных и т.д. >
Сейчас я следующий код:
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));
Сообщение об ошибке: «Метод положить в интерфейсной карте < K, V > не может быть применен к данным типам Обязательно:. Строка, CAP # 1 Найдено: String, Object
Да, newInstance() создает объекты и вероятно, должен быть какой-то актерский состав, но для чего? Я не хочу просто «(Dict)» здесь, я бы предпочел «(Plant)», «(Animal)» ... по мере необходимости. Как это сделать?
Вы возвращаете 'Map> '. Таким образом, клиентский код не может знать, содержат ли карты растения, кошки или что-то еще. Все, что он знает, это то, что они являются экземплярами Дикта. Поэтому бросьте его на Дикта, и все будет хорошо. Тем не менее, я бы не использовал рефлексию для этого, а фабрики (один завод на тип Dict), что позволило бы делать то, что вы хотите. –
Но животное может бежать(), пока растение не может. Если я передам его в Dict, у меня нет возможности запускать run(), кроме ручного литья в Animal. Что касается подхода «один завод за диктатор»: вы имеете в виду, что у меня должен быть «публичный статический состав (строковый дескриптор)» в каждом из подтипов Dict и вместо вызова «bob.newInstance (handle)»: dict. put (handle, c.getMethod ("fabricate", Class.forName ("java.lang.String")))? Это дает ту же ошибку. – Forseti
Когда у вас есть 'Map ', у вас нет способа узнать, что карта содержит в любом случае. Все, что вы можете сделать, это проверить, является ли это значение экземпляром Plant или Animal (с использованием 'instanceof'), и передать значение для Plant или Animal. Итак, что вы получаете от создания карты «Карта », так как вы возвращаете ее как «Map '.Ваша фабричная идея действительно является решением. Вам понадобится метод возврата Plant или Animal (в зависимости от класса), и вам понадобится карта типа «Map ' или типа «Map », в зависимости от в каталоге. –