2012-01-28 14 views
4

如果我有這樣的方法(爲簡單起見,假設整數):如何返回正確的列表類型?

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

,我需要爲我的處理在內部創建一個新的列表,我將創建並以某種方式填充,並返回給調用者,我怎麼能這樣做,因爲我不知道調用者通過什麼類型的列表?

我不想返回不同類型的List什麼呼叫者通過。

例如如果來電者通過了LinkedList而我不要想要返回ArrayList

如何最好地接近這個問題?

+0

爲什麼您確切地需要返回相同類型的東西?是不是人們會用'ArrayList a = doSomething(a);'來調用它?在這種情況下它會自動被強制轉換? – kba 2012-01-28 16:07:15

+0

爲什麼他們會用'ArrayList a ='調用它?其他回答已經投訴的朋友/指出不應該有一個'ArrayList'在任何地方浮動(即使用接口) – Cratylus 2012-01-28 16:12:02

+2

@KristianAntonsen這沒有意義;如果我傳遞一個'LinkedList',我不能隨機將返回值轉換爲任意實現並期望它可以工作。 – 2012-01-28 16:12:38

回答

4

如果你可以逃脫只是使用這些兩個輸出類型之一,那麼你可以做

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

RandomAccess接口是爲了表明實現允許O(1)get操作。

標記接口使用List實現來指示它們支持快速(通常是恆定時間)的隨機訪問。該接口的主要目的是允許通用算法改變其行爲,以便在應用於隨機或順序訪問列表時提供良好的性能。

通過這樣做,您的API可以讓客戶端捍衛他們的輸入。他們可以通過Collections.unmodifiableList(...)的結果,並確保它沒有被其他代碼修改。

如果你真的知道輸入是一個可變列表,你可以在clone()的列表中,然後clear()它。 ArrayListLinkedList都有公開的clone()方法,可以反射地訪問。

+0

我想到了,但'List'沒有提供'clone()' – Cratylus 2012-01-28 15:57:33

+0

@ user384706,如果您知道您的輸入將具有公共克隆方法,則可以反射地訪問'clone'方法。 – 2012-01-28 16:00:17

+0

我喜歡這個解決方案。但是,它引發了一個問題:如果用戶確實傳入了一個不可修改的列表,這個方法是否也返回了一個不可修改的列表?而且,如果這是期望的行爲,那麼您如何確定通過的列表是不可修改的? – user949300 2012-01-28 16:31:11

7

您不應該將您的實現與List的特定實現綁定在一起,使用接口的想法是,從外部看,只要它符合實例化的具體類應該無關緊要List接口。

編輯:

不管怎麼說,這是一個可能的方式:

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

我承認你在說什麼,但是通常使用'List'但是假設你有一個'ArrayList'在默認情況下(我的意思是我的擔心是某處出現ClassCastException) – Cratylus 2012-01-28 16:04:53

+0

另外,交換列表實現可能會影響其他地方的性能。 – Cratylus 2012-01-28 16:06:23

+0

一些實現*會*期待某些類型的列表,但主要是隨機訪問列表。 – 2012-01-28 16:07:12

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所指出的那樣,如果客戶通過一個不可修改的包裝,這種做法就不會奏效。 – user949300 2012-01-28 16:19:32

+0

@ user949300:沒錯,如果有可能使用不可修改的包裝,我建議與邁克塞繆爾斯的解決方案一起。 – esaj 2012-01-28 16:27:36

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。 – 2012-02-01 06:57:35