Предупреждение о безопасности типа Java Generics с рекурсивным 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 для безопасности типов компиляции, и если я подавляю предупреждение, то это побеждает цель.

Спасибо за ваши комментарии, ксб


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 (для меня это выглядит как дерево), который вместо этого содержит карту. Что-то типа:

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

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

И пусть foo вместо этого принимает Node в качестве второго аргумента. Просто предложение.

person waxwing    schedule 14.03.2010
comment
дорогой свиристель, в итоге я реализовал твое второе предложение, потому что оказалось, что мне нужно добавить в узел дополнительные вещи, а не просто ссылку на другую хэш-карту. Это также чувствует себя намного более естественным. Я только начинаю разбираться с Java, поэтому не могу понять ваше первое предложение - T расширяет часть Map‹String,T›. Еще раз спасибо, КСБ - person GC.; 15.03.2010

Вы можете использовать этот класс вместо HashMap:

public class RecursiveHashMap extends HashMap<String,RecursiveHashMap>
{
}
person Ha.    schedule 14.03.2010
comment
Уважаемый Ха, Не очень понимаю, почему это может работать (Java noob), поэтому не пробовал. Я использую решение Node от Waxwing. Спасибо, КСБ - person GC.; 15.03.2010
comment
Да, создание класса Node (он же составной шаблон) намного лучше, чем HashMap. - person Ha.; 15.03.2010

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

Я бы предложил использовать составной шаблон (см. Википедию), упрощенный код:

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, который вы использовали, сводится к набору, появляющемуся в DirectoryNode.

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