2012-01-28 4 views
4

Если у меня есть метод, как это (для простоты предположим, целые числа):Как вернуть правильный тип списка?

public static List<Integer> doSomething(List<Integer> list) { 
    // logic here 
} 

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

Я не хочу возвращать List различного типа, что вызывающий абонент перешел в.

Э.Г. Если вызывающий абонент прошел LinkedList и I не хотите вернуть ArrayList.

Как можно подойти к этой проблеме?

+0

Почему именно вам нужно вернуть что-то подобное? Не будут ли люди называть это чем-то вроде 'ArrayList a = doSomething (a);' и в этом случае он будет автоматически запущен? – kba

+0

Зачем им называть его «ArrayList a ='? Другие друзья, которые ответили уже, жаловались/указали, что не должно быть 'ArrayList', плавающих где-нибудь (т. Е. Использование интерфейсов) – Cratylus

+2

@KristianAntonsen Это не имеет смысла ; если я перейду в «LinkedList», я не могу случайным образом применить возвращаемое значение к произвольной реализации и ожидать, что он будет работать. –

ответ

4

Если вы можете уйти только с помощью одного из этих двух типов выходных, то вы можете сделать

if (inputList instanceof RandomAccess) { 
    // use an ArrayList 
} else { 
    // use a LinkedList. 
} 

Интерфейс RandomAccess предназначен для указания того, что реализация позволяет O (1) get операции.

Интерфейс маркера, используемый List реализациями, указывающими, что они поддерживают быстрый (обычно постоянное время) случайный доступ. Основная цель этого интерфейса - дать возможность универсальным алгоритмам изменять их поведение, чтобы обеспечить хорошую производительность при применении к случайным или последовательным спискам доступа.

Таким образом, ваши API-интерфейсы позволяют клиентам защищать свои входы. Они могут передаваться в результате Collections.unmodifiableList(...) и быть уверены, что они не будут изменены другим кодом.

Если вы действительно знаете, что ввод является изменяемым списком, вы можете clone() списка, затем clear(). И ArrayList, и LinkedList имеют общедоступные методы clone(), к которым можно получить доступ отражательно.

+0

Я думал об этом, но 'List' не предлагает' clone() ' – Cratylus

+0

@ user384706, метод' clone' можно получить обратимо, если вы знаете, что ваш вход будет иметь общедоступный метод клонирования. –

+0

Мне нравится это решение. Тем не менее, он задает вопрос: если пользователь действительно переходит в unmodifiableList, этот метод также возвращает unmodifiableList? И, если это желаемое поведение, как вы решаете, не прошел ли измененный список? – user949300

7

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

EDIT:

Во всяком случае, вот возможный путь:

List<Integer> lst1 = new ArrayList<Integer>(); 
Class<?> klass1 = lst1.getClass(); 
List<Integer> copy1 = (List<Integer>) klass1.newInstance(); 
System.out.println(copy1.getClass().getName()); 
> java.util.ArrayList 

List<Integer> lst2 = new LinkedList<Integer>(); 
Class<?> klass2 = lst2.getClass(); 
List<Integer> copy2 = (List<Integer>) klass2.newInstance(); 
System.out.println(copy2.getClass().getName()); 
> java.util.LinkedList 

Как вы можете видеть в консоли, копии являются экземплярами одного и того же класса, что и исходный список.

+0

Я принимаю то, что вы говорите, но обычно используется «Список», но предположим, что по умолчанию у вас есть «ArrayList» (я имею в виду, что моя проблема - это исключение ClassCastException где-то). – Cratylus

+0

Также реализация списка замены может повлиять на производительность в другом месте , – Cratylus

+0

Некоторые реализации * do * ожидают определенные типы списков, хотя, в частности, списки произвольного доступа. –

0

Вы можете использовать Class.newInstance, чтобы создать список пропущенный по типу:

public static List<Integer> doSomething(List<Integer> list) 
{ 
    List<Integer> newList = null; 
    try 
    { 
     newList = list.getClass().newInstance(); 
    } 
    catch(InstantiationException e) 
    { 
     throw new RuntimeException(e); 
    } 
    catch(IllegalAccessException e) 
    {  
     throw new RuntimeException(e); 
    } 

    //Logic here 

    return newList; 
} 

@Test 
public void test() 
{  
    List<Integer> testList = new ArrayList<Integer>(); 

    List<Integer> resultList = doSomething(testList); 
    Assert.assertEquals(testList.getClass(), resultList.getClass()); 
    Assert.assertNotSame(LinkedList.class, resultList.getClass()); 

    testList = new LinkedList<Integer>(); 

    resultList = doSomething(testList); 
    Assert.assertEquals(testList.getClass(), resultList.getClass()); 
    Assert.assertNotSame(ArrayList.class, resultList.getClass());  
} 
+0

Я только что опубликовал (затем удалил) что-то очень похожее. Как указывает @Mike Samuel, это не будет работать так хорошо, если клиенты перейдут в Unmodifiable wrapper. – user949300

+0

@ user949300: Правда, если есть вероятность, что используется немодифицируемая оболочка, я бы предложил пойти с решением Майка Сэмюэлса. – esaj

0

Если вы действительно, действительно все равно, какой объект выходит, я бы включать в себя, что в качестве параметра метода, как :

<T extends List<Integer>> T doSomething(Class<T> returnType,List<Integer> v) 
    throws Exception 
{ 
    // constructors for your return will be tricky :) 
    // returnType.newInstance() will probably work. 
    T result = returnType.newInstance(); 
    result.add(86); result.add(99); 
    return result; 
} 
1

Лучшее, что нужно сделать, это удалить создание списка из метода. Попросите абонента решить, как создать список:

public static void doSomething(List<Integer> dest, List<Integer> src) { 
+0

Мне нравится этот подход. Лучше, чем в зависимости от newInstance в методе. –

Смежные вопросы