Java Generics Type Safety предупреждение с рекурсивна Hashmap

Използвам рекурсивно дърво на hashmaps, по-специално Hashmap карта, където Object е препратка към друга Hashmap и т.н. Това ще бъде предадено около рекурсивен алгоритъм:

foo(String filename, Hashmap<String, Object> map)
{
    //some stuff here
    for (Entry<String, Object> entry : map.entrySet()) 
    {
       //type warning that must be suppressed
       foo(entry.getKey(), (HashMap<String, Object>)entry.getValue());
    }
}

Знам със сигурност, че Object е от тип Hashmap<String, Object>, но съм раздразнен, че трябва да потискам предупреждението с помощта на @SuppressWarnings("unchecked").

Ще бъда доволен от решение, което прави или assert(/*entry.getValue() is of type HashMap<String, Object>*/), или хвърля изключение, когато не е така. Тръгнах по маршрута на Generics за безопасност на типа на компилиране и ако потисна предупреждението, то проваля целта.

Благодаря ви за коментарите, ksb


person GC.    schedule 14.03.2010    source източник


Отговори (3)


Това е възможно с помощта на общ метод с променлива от рекурсивен тип. Опитайте следното:

public <T extends Map<String, T>> void foo(String filename, T map) {
    //some stuff here
    for (Map.Entry<String, T> entry : map.entrySet())  {
        foo(entry.getKey(), entry.getValue());
    }
}

Трябва да се компилира добре без никакви предупреждения.

Въпреки това, ако имате контрол върху картата и можете да замените свой собствен клас, може да е по-разбираемо да направите клас Node (това ми прилича на дърво), който вместо това съдържа Map. Нещо като:

public class Node {
    private Map<String, Node> children;

    ...
    // accessor methods to retrieve children ...
}

И накарайте foo да вземе Node като втори аргумент вместо това. Само едно предложение.

person waxwing    schedule 14.03.2010
comment
скъпа waxwing, в крайна сметка приложих второто ти предложение, защото се оказа, че трябва да добавя допълнителни неща към възела, а не просто препратка към друга хеш карта. Освен това се чувства много по-естествено. Тъкмо започвам да се справям с Java, така че не мога да разбера първото ви предложение - частта T разширява Map‹String,T›. Благодаря ви още веднъж, KSB - person GC.; 15.03.2010

Можете да използвате този клас вместо HashMap:

public class RecursiveHashMap extends HashMap<String,RecursiveHashMap>
{
}
person Ha.    schedule 14.03.2010
comment
Уважаеми Ха, наистина не разбирам защо това може да работи (Java noob), затова не го изпробвах. Ще използвам решението Node на waxwing. Благодаря, KSB - person GC.; 15.03.2010
comment
Да, създаването на Node клас (известен още като Composite pattern) е много по-добро от HashMap. - person Ha.; 15.03.2010

Вашата структура от данни изглежда така, сякаш искате да представите дървета от файлове (имена на файлове) с нея. Не бих препоръчал да правите това с HashMap като тип възел.

Бих предложил да използвате съставния модел (вижте wikipedia), опростен код:

abstract class Node
{
  String filename;
  Node( String filename ) { this.filename = filename; }
  abstract foo();
}

class FileNode implements Node
{
  FileNode( String filename ) { super(filename); }
  foo() { ... }
}

class DirectoryNode implements Node 
{
  Set<Node> children;
  DirectoryNode( String filename, Set<Node> children )
  {
    super(filename);
    this.children = children;
  }
  foo()
  {
    for ( Node child : children ) child.foo();
  }
}

HashMap, който сте използвали, се свежда до Set, който се появява в DirectoryNode.

person Wolfgang    schedule 14.03.2010
comment
Благодаря за мисълта. Моите обекти не могат да бъдат естествено разделени на два типа възли, т.е. директория и файлове. Всички те са файлове. По-конкретно трябва да направя същата обработка foo() на всички слоеве на йерархията. Не мога да разширя вашия код, за да постигна това. - person GC.; 15.03.2010
comment
Защо не? Можете да поставите целия код, от който се нуждаете, в DirectoryNode.foo(). Можете също да поставите кода в Node.foo() и след това да извикате super.foo() на DirectoryNode.foo(). Тогава Node ще бъде вашият файл. - person Wolfgang; 15.03.2010
comment
Разбирам какво казвате: Поставете общия код вътре в Node (foo вече не е абстрактен) и накарайте File и Directory да извикат super(filename). Ще запомни този. Благодаря, ksb - person GC.; 15.03.2010