2011-02-02 1 views
10

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

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

+2

Зачем вам нужна ваша выходная коллекция того же типа, что и входной? –

+0

Если оригинальная коллекция не изменена? – Puce

+0

@Nicolas: Вопрос удобства :) – Rnet

ответ

2

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

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

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

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

+1

см. Мой комментарий к сообщению michael о методе clone и интерфейсе cloneabl. – Nicolas

+1

Да, # 2 более разумно – Rnet

0

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

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

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

Ans so on.

+2

Завтра любой может реализовать Collection. –

0

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

+0

Объект.clone() защищен. Вы не можете просто называть его, если вы не знаете реальный тип объекта. Ну, может быть, вы можете сделать это, используя рефлексию, как предлагал в своем ответе biziclop. –

+0

Исправлена ​​ошибка - забыли, что вам нужно реализовать 'cloneable' и переопределить' clone() '. –

+1

Даже если вы реализуете 'Cloneable', метод clone может быть неактивным, см. Javadoc http://download.oracle.com/javase/6/docs/api/java/lang/Cloneable.html – Nicolas

9

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


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

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

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

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

Довольно много реализаций 'Collection' do, поэтому метод' instanceof' будет работать во многих случаях. –

+0

Посмотрите еще комментарий: Cloneable не означает, что вы можете использовать метод clone. – Nicolas

+0

Ссылка на немодифицируемую коллекцию останется локальной, исходная коллекция по-прежнему модифицируется по ссылкам снаружи, я хотел изменить копию оригинальной коллекции и вернуть ее. – Rnet

4

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

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

Скала РЕПЛ говорит мне, что theClone имеет статический тип Object (вы можете бросить это Collection[Int] или LinkedList[Int]), но динамический тип клона еще LinkedList.

Теперь предположит, что вы хотите, это метод, который возвращает статический тип LinkedList когда он recieves статического типа LinkedList и возвращает статический тип ArrayList когда он recieves статического типа 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(); 
} 
1

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

3

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

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; 
    } 
+3

FYI, это происходит отсюда: http://code.google.com/p/google-collections/source/browse/trunk/src/com/google/ common/base/Objects.java? spec = svn16 & r = 9 - мы позже удалили его, потому что это ужасный бесполезный грязный хакерский хак. –