Обобщения Java: соответствие типа параметра - PullRequest
1 голос
/ 03 марта 2012

Как бы я изменил это так, чтобы итоговая Коллекция (newNodes) была того же типа, что и входящая (узлы)?

public void setNodes(Collection<NodeInfo> nodes) {
    Collection<NodeInfo> newNodes = new TreeSet<NodeInfo>();
    for (NodeInfo ni: nodes) {
        newNodes.add(ni.clone());
    }
}

Я подозреваю, что это что-то вроде ...

public void setNodes(<T extends Collection<NodeInfo>> nodes) {
    Collection<NodeInfo> newNodes = new T<NodeInfo>()

Возможно ли это?

Ответы [ 4 ]

2 голосов
/ 03 марта 2012

Близко, но без сигары.Если я понимаю, что вы хотите сделать, ваш метод должен выглядеть так:

public <T extends NodeInfo> void setNodes(Collection<T> nodes) {
    Collection<T> newNodes = new TreeSet<T>();
    for(T t : nodes) {
        newNodes.add(t);
    }
}
2 голосов
/ 03 марта 2012

К сожалению, это невозможно, так как вы написали это на Java.Если вам нужен этот эффект, у вас есть несколько вариантов:

Если вы пытаетесь оптимизировать для определенного вида коллекции, вы можете использовать проверку instanceof для его обнаружения.(Например, библиотеки Guava часто делают это, чтобы обнаружить неизменяемые коллекции и обрабатывать их специально.)

Если вам действительно нужна одна коллекция для заполнения, вы можете попросить вызывающую сторону предоставить вам одну.

public <C extends Collection<NodeInfo>> void setNodes(C nodes, C newNodes) {
  for (NodeInfo ni : nodes) {
    newNodes.add(ni);
  }
}

Если вам нужна возможность создавать произвольное количество этих коллекций по требованию, то вы можете определить фабричный интерфейс и заставить вызывающего пользователя предоставить его экземпляр:

interface Factory<C extends Collection<NodeInfo>> {
  C newCollection();
}

public <C extends Collection<NodeInfo>> void setNodes(C nodes, Factory<C> factory) {
  C newNodes = factory.newCollection();
  for (NodeInfo ni : nodes) {
    newNodes.add(ni);
  }
}
1 голос
/ 03 марта 2012

К сожалению, вы не можете сделать new T в Java: поскольку универсальные элементы реализованы в Java с помощью стирания типа, информация о типе, предоставляемая параметром типа, является только статически используемой информацией, то есть больше не доступна во время выполнения.Следовательно, Java не разрешает создание объектов в общем виде (см. Общие вопросы Анжелики Ланге ).

В качестве альтернативы вы можете использовать:

  • маркер типа, т.е. использовать объект Class<T> в качестве параметра, чтобы сделать тип доступным во время выполнения
  • использовать подписьvoid setNodes(Collection<NodeInfo> nodes, Collection<NodeInfo> newNodes), если вы можете создать подходящую Коллекцию в другом месте.
  • использовать стандартную реализацию Коллекции, если она подходит, например, ArrayList<NodeInfo>
  • глубокий клон nodes, например, используя Библиотека клонирования :

    Cloner cloner = new Cloner ();

    @ SuppressWarnings ("unchecked") Collection<NodeInfo> newNodes = cloner.deepClone(nodes);

0 голосов
/ 04 марта 2012

Обратите внимание, что многие из Collection реализаций в JDK сами реализуют Cloneable. Один из подходов «наилучшего усилия» может быть таким:

public Collection<NodeInfo> setNodes(Collection<NodeInfo> nodes) throws CloneNotSupportedException {
    Collection<NodeInfo) newNodes;

    if (nodes instanceof Cloneable) 
        newNodes = (Collection<NodeInfo>) newNodes.clone();
    else
        // Fallback in case we have a non-cloneable collection
        newNodes = new TreeSet<NodeInfo>();

    newNodes.clear();
    for (NodeInfo ni: nodes) {
        newNodes.add(ni.clone());
    }
    return newNodes;
}

Это возвращает один и тот же вид коллекции для многих входных данных, но вернется к возвращению TreeSet в качестве значения по умолчанию, если оно не может работать лучше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...