Карта на различни видове карти

Трябва да инициализирам няколко обекта от различни типове от 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)“ тук, бих предпочел „(Растение)“, „(Животно)“... според случая. Как да стане това?


person Forseti    schedule 24.02.2013    source източник
comment
Връщате Map<String, Map<String, ? extends Dict>>. Така че клиентският код не може да знае дали картите съдържат растения, котки или каквото и да било. Всичко, което знае е, че те са екземпляри на Dict. Така че го качете на Dict и всичко ще бъде наред. Това каза, че не бих използвал отражение, за да направя това, а фабрики (една фабрика за тип Dict), което ще позволи да правите това, което искате.   -  person JB Nizet    schedule 24.02.2013
comment
Но Animal може да работи(), докато Plant не може. Ако го кастирам към Dict, нямам възможност да извикам run(), освен първо ръчно кастинг към Animal. Що се отнася до подхода „една фабрика на Dict“: искате да кажете, че трябва да имам „public static fabricate(String handle)“ във всеки от подтиповете Dict и вместо да извиквам „bob.newInstance(handle)“, за да направя: dict.put(handle , c.getMethod(fabricate, Class.forName(java.lang.String))) ? Това дава същата грешка.   -  person Forseti    schedule 24.02.2013
comment
Когато имате Map<String, ? extends Dict>, така или иначе няма начин да знаете какво съдържа картата. Всичко, което можете да направите, е да тествате дали стойността е екземпляр на растение или животно (с помощта на instanceof) и да прехвърлите стойността към растение или животно. И така, какво печелите, като направите картата Map<String, Animal>, след като я връщате като Map<String, ? extends Dict>. Вашата идея за фабрика наистина е решение. Ще ви е необходим методът за връщане на растение или животно (в зависимост от класа) и ще трябва картата да е от тип Map<String, Plant> или от тип Map<String, Animal>, в зависимост от директорията.   -  person JB Nizet    schedule 24.02.2013


Отговори (1)


Прехвърляне на обекта към Dict. Това е предимството на plolymorphism - не е нужно да се позовавате на конкретни подкласове. Можете да напишете своя код с препратки само към единичния базов клас, Dict. Това ви дава:

  • a) Слабо свързване - няма зависимости по време на компилиране на конкретни подкласове
  • b) Разширяемост - можете да добавяте подкласове, без да се налага да променяте този код.

Да, има начини да постигнете това, което поискахте (идва на ум голямо изявление за превключване), но нищо, което бих препоръчал.

person EJK    schedule 24.02.2013