2014-10-30 71 views
4

假設我們有以下幾點:Java列表<Parent>和列表<Children>方法

public class Parent{ 
    public void setXXX(String XXX); 
    public String getXXX(); 
} 

public class Children extends Parent{ 
.... 
} 

現在我想創建一個像下面這樣的方法稱爲克隆列表:

public List<Something> cloneList(List<Something> original){ 
    List<Something> newList=new ArrayList<>(); 
    for(Something e:original){ 
     Something newE=new Something(); 
     newE.setXXX(e.getXXX()); 
     newList.add(newE); 
    } 
    return newList; 
} 

的事情是我們要cloneList可以應用於List<Parent>List<Children>,所以無論如何適用於「某事」?

東西不能「擴展父?」或「父」,由於Java的Collection<Parent>Collection<Children>

假設不兼容: 1.不要想用任何方式序列化或反射。

  1. 我們無法修改父類和子類。這是在第三方Jar中預定義的。

  2. SuperParent類是不可能的,因爲在2

+1

考慮一個事實,即您不能在您的帖子中發佈< >括號除非你將其格式化爲代碼。 – 2014-10-30 15:10:54

+0

使用泛型... – brso05 2014-10-30 15:15:10

+1

沒有解決方案滿足您添加的所有約束。要創建其類不是靜態已知的對象的忠實副本,您需要使用靜態已知可用的反射或方法。如果這些類已經有可用的複製方法,那麼你肯定會在你的示例代碼中使用它。 (請注意''Object.clone()'可以達到這個目的,但是隻有當類'Parent'實現'Cloneable'時,即使這樣做,Object.clone()可能會很成問題。) – 2014-10-30 16:14:03

回答

0

變化cloneList到簽名聲明,我們不能修改父:

public <X extends Parent> List<X> cloneList(final List<X> original) 

然後它會工作,至少在方法簽名。您可以在內部構建一個List<Parent>,然後將其轉換爲List<X>,並在需要時忽略警告;沒有辦法找出「X」的運行時類型。

1

你不能使用泛型這種方式,只能反射。

對於類型變量T,不能使用new T()。這是因爲泛型是編譯時機制,並且在運行時使用new來創建特定類型的對象,並且編譯器無法在編譯時創建對該類型的適當引用。因此,儘管這樣的:

new ArrayList<T>(); 

是合法的,因爲編譯器實際上編譯成的代碼創建的原始ArrayList類型,這樣的:

new T(); 

不是,因爲編譯器甚至不知道是什麼實際的類將是(即使它只是被定義爲T extends Parents它可能是一個甚至沒有編寫程序時編寫的類,如Grandchildren或其他),甚至不知道它是否具有無參數構造函數。

2

這在Java中是不可能的。看看Generic syntax for extends or equal to

你可以改變你的方法如下,讓你的父類擴展SuperParent。

public static <T extends SuperParent> List<T> cloneList(List<T> original, Class<T> type) throws IllegalAccessException, InstantiationException { 
    List<T> newList=new ArrayList<>(); 
    for(T e : original){ 
     T x = type.newInstance(); 
     x.setXXX(e.getXXX()); 
     newList.add(x); 
    } 
    return newList; 
} 

此外,您可以選擇其他克隆方法。例如,使用Apache的百科全書SerializationUtils

List<Children> result = (List<Children>) SerializationUtils.clone(originalList); 
+1

同意更多關於序列化的建議(或者可怕的'克隆',但忘記我剛剛說過的),而不是基於反思的建議。首先,它假設存在一個無參數的構造函數。其次,新副本雖然具有正確的類型,但不具備原件的完整內部狀態,這就是爲什麼序列化更受歡迎的原因。 – RealSkeptic 2014-10-30 15:47:29

0

在一般意義上,你應該能夠使用具有這種簽名的方法:

public <T extends Parent> List<T> cloneList(List<T> original) 

這不是你最大的問題,但是。這將獲得列表元素的副本。您的代碼不能使用

T newE = new T(); // doesn't work 

因爲類型參數T的空構造函數的存在不能保證。相反,您需要一種能夠返回正確類型副本的方法。你不能完全安全地做到這一點,但你可以靠近。您可以實現這些方法:

public Parent Parent.copy(); 
public Children Children.copy(); 

...以任何方式是適當的,然後寫出像這樣的方法:

public <T extends Parent> List<T> cloneList(List<T> original) { 
    List<T> newList = new ArrayList<>(); 

    for (T originalItem : original) { 
     newList.add(original.getClass().cast(original.copy())); 
    } 

    return newList; 
} 

(請注意,雖然Object.getClass()的記錄返回類型爲Class<?>,這對於這個目的不起作用,method documentation表示返回類型實際上比這個更具體一些,足以使這項工作成功。)

+0

'new T();'不能僅僅因爲空構造函數而被使用,而且因爲你不能實例化一個類型變量。但更重要的是,如果類中有任何最終字段,則'copy()'方法可能無法正確地複製內部狀態。複製構造函數將是需要的,然後您需要使用'getConstructor(...)'。 – RealSkeptic 2014-10-30 16:17:03

+0

@RealSkeptic我提出的'copy()'方法不是將狀態從一個對象複製到另一個對象,而是返回調用該對象的副本。它可以做任何它需要做的事情,包括調用一個拷貝構造函數。它不需要使用反射,因爲它是對象類的實例方法,因此它知道要使用什麼構造函數以及如何實例化該類的對象。 – 2014-10-30 17:15:04