Java: клонировать произвольный сбор через ссылку на Collection

Предположим, что у вас есть ссылка типа java.util.Collection в методе и не могу сказать, какую реализацию java.util.Collection он укажет на время выполнения, можно ли клонировать сборку?

Я хотел реализовать универсальный метод, который будет фильтровать любой тип данных. Следовательно, метод будет принимать java.util.Collection в качестве входных данных. Однако помимо этого я не хотел изменять оригинальную коллекцию, поэтому я хотел клонировать коллекцию.

Ответ 1

Я вижу три варианта:

  • Полагайтесь на собственный метод коллекции clone (при условии, что он реализует Cloneable), а затем удаляет нежелательные элементы. Изменить: как указано в комментариях и других ответах, clone() не является общедоступным и, следовательно, недоступен.

  • Попросите абонента предоставить пустую коллекцию для копирования целевых элементов между источником и получателем.

  • Определите интерфейс factory для создания пустой коллекции и попросите вызывающего пользователя предоставить реализацию factory. Затем скопируйте целевые элементы между источником и получателем.

Ответ 2

К сожалению, интерфейс Collection ничего не говорит о реализации Clonable Interface.


Но то, что вы всегда можете сделать, это скопировать коллекцию:

List<T> copy = new ArrayList<T>(original);

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

Collection<T> unmodifiable = Collections.unmodifiableCollection(original);

Ответ 3

Я собираюсь продемонстрировать в Scala, потому что у него есть REPL, где я могу протестировать, но одна и та же семантика должна работать на Java.

import java.util._
val orig = new LinkedList[Int]
val theClone = orig.clone

Scala REPL сообщает мне, что у theClone есть статический тип Object (вы можете применить это к Collection[Int] или LinkedList[Int]), но динамический тип клона все еще LinkedList.

Теперь я полагаю, что вам нужен метод, возвращающий статический тип LinkedList, когда он получает статический тип LinkedList и возвращает статический тип ArrayList, когда он получает статический тип ArrayList и т.д. в какой случай

def doClone[C <: Collection[_]](orig:C) = {
  val cloneMethod = orig.getClass.getDeclaredMethod("clone")
  if (cloneMethod.isAccessible)
    cloneMethod.invoke(orig).asInstanceOf[C]
  else
    throw new CloneNotSupportedException
}

В Java, я думаю, что это

<C extends Collection<?> > C doClone (C orig) {
   java.lang.reflect.Method cloneMethod = 
     orig.getClass().getDeclaredMethod("clone");
   if (cloneMethod.isAccessible())
     return (C) cloneMethod.invoke(orig);
   else
     throw new CloneNotSupportedException();
}

Ответ 4

Если вы действительно, действительно, действительно, действительно нужно это сделать, есть уродливый хак.

  public static <T> T tryToClone(T object)
      throws CloneNotSupportedException {
    Object clone = null;

    // Use reflection, because there is no other way
    try {
      Method method = object.getClass().getMethod("clone");
      clone = method.invoke(object);
    } catch (InvocationTargetException e) {
      rethrow(e.getCause());
    } catch (Exception cause) {
      rethrow(cause);
    }
    if (object.getClass().isInstance(clone)) {
      @SuppressWarnings("unchecked") // clone class <= object class <= T
      T t = (T) clone;
      return t;
    } else {
      throw new ClassCastException(clone.getClass().getName());
    }
  }

  private static void rethrow(Throwable cause)
      throws CloneNotSupportedException {
    if (cause instanceof RuntimeException) {
      throw (RuntimeException) cause;
    }
    if (cause instanceof Error) {
      throw (Error) cause;
    }
    if (cause instanceof CloneNotSupportedException) {
      throw (CloneNotSupportedException) cause;
    }
    CloneNotSupportedException e = new CloneNotSupportedException();
    e.initCause(cause);
    throw e;
  }

Ответ 5

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

Ответ 6

В теории это возможно с отражением, однако не все Collection реализации могут (или должны) быть созданы таким образом. Первым примером является результат Collections.singletonList(), который вообще не имеет общих конструкторов. Другие специальные коллекции могут также поднимать и другие проблемы.

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

Collection c = ...
if( c instanceof SortedSet )
  return new TreeSet( c );
if( c instanceof Set )
  return new HashSet( c );

Ans и так далее.

Ответ 7

Если коллекция реализует Cloneable, вы можете это сделать. Вам не нужно беспокоиться о точном типе; реализация коллекции clone() позаботится об этом.