Трябва да инициализирам няколко обекта от различни типове от XML дефиниции до бобове. Имам структура на директорията:
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 Found: 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>
. Вашата идея за фабрика наистина е решение. Ще ви е необходим методът за връщане на растение или животно (в зависимост от класа) и ще трябва картата да е от типMap<String, Plant>
или от типMap<String, Animal>
, в зависимост от директорията. - person JB Nizet   schedule 24.02.2013