2010-01-08 42 views
2
List <ClassA> listA; List <ClassB> listB; 

我可以使用反射將listA反射到listB中嗎?我得到下面的代碼,但只反映了一個對象將列表值反映到另一個列表中?

public static <A, B> B convert(A instance, 
           Class<B> targetClass) throws Exception { 
    B target = (B)targetClass.newInstance(); 

    for (Field targetField : targetClass.getDeclaredFields()) { 
     targetField.setAccessible(true); 
     Field field = 
      instance.getClass().getDeclaredField(targetField.getName()); 
     field.setAccessible(true); 
     targetField.set(target, field.get(instance)); 
    } 
    return target; 
} 
+0

不要這樣做。你在這裏做什麼是非常奇怪的。如果這兩種類型具有共同的操作,使它們實現一個通用的接口。 – 2010-01-08 22:25:53

回答

3

一般而言,convert方法不可能工作(對於List或任何其他類型)。

調用Field.setAccessible(true)允許讀取和寫入私人字段,但不允許通過Field.set()(引發IllegalAccessException: Field is final異常)修改final字段。

根據List的實施情況,您嘗試複製此操作可能會妨礙其正常工作。例如,使用ArrayList如:

// Note that this is an unchecked cast 
List<ClassB> listB = (List<ClassB>) convert(listA, ArrayList.class); 

試圖複製serialVersionUID時失敗。

以下變化張貼代碼獲取圓這個問題static final serialVersionUIDArrayList

public static <A, B> B convert(A instance, 
          Class<B> targetClass) throws Exception { 
    B target = (B)targetClass.newInstance(); 

    for (Field targetField : targetClass.getDeclaredFields()) { 
     targetField.setAccessible(true); 
     Field field = 
      instance.getClass().getDeclaredField(targetField.getName()); 
     field.setAccessible(true); 

     // Ignore attempts to set final fields 
     try { 
      targetField.set(target, field.get(instance)); 
     } catch (IllegalAccessException e) { 
      continue; 
     } 
    } 

    return target; 
} 

然而,接下來的問題是,convert方法進行淺拷貝。對於不同類型的List s,此更改後的版本convert看起來可能正常工作,但它不會將列表中的ClassA對象轉換爲ClassB(未經檢查的轉換會隱藏此對象)。這可能會導致稍後在應用程序中拋出ClassCastException

解決這個問題可以通過添加另一種方法來實現包裝convert

public static <A, B extends List<C>, C> B convertList(
    List<A> list, Class<B> targetListClass, Class<C> targetClass) 
    throws Exception { 

    B targetList = targetListClass.newInstance(); 

    for (A object : list) { 
     targetList.add(convert(object, targetClass)); 
    } 

    return targetList; 
} 

這一操作將需要被稱爲:

List<ClassB> listB = (List<ClassB>) convertList(
    listA, ArrayList.class, ClassB.class); 
+0

您好馬修默多克你可以向我解釋爲什麼會導致失敗時,試圖複製serialVersionUID時反映列表和一個對象不會得到這個錯誤。你的意思是淺拷貝?謝謝你veyr多 – user236501 2010-01-13 11:01:11

1

試試吧。

但它不是必需的。您可以第一個列表中的內容只是添加到第二個:

List list2 = new ArrayList(list1); 

,因爲它似乎,你的兩個List s的不同泛型類型聲明。所以你將不得不使用不安全的強制轉換。但是這樣做感覺非常糟糕。你究竟在努力實現什麼?如果列表中的對象類型應該是不同的,那麼你爲什麼要複製它們?

+0

對不起,我沒有發現我的沒有顯示出來。基本上是兩個List不同類別的對象 – user236501 2010-01-08 07:24:57

+0

其實這兩個Class有相同的屬性,由於某種原因必須將ClassB轉換爲ClassA – user236501 2010-01-08 08:44:10

+0

然後使用我給你的門檻。如果編譯器有抱怨,請使用強制轉換 – Bozho 2010-01-08 08:48:12

0

從技術上說,是的,但只能在特定的條件。假設您想將ClassA實例轉換爲ClassB實例。開始轉換的代碼行如下所示:

ClassB bInstance = convert(aInstance, ClassB.class); 

我到目前爲止是否正確?

上述代碼將(1)使用默認的構造函數創建一個新的ClassB對象,(2)獲取在ClassB中聲明的所有字段(但不包括任何ClassB超類型的no字段!),(3)從ClassA中獲取相同的字段,(4)將值從aInstance設置爲新創建的ClassB實例中的字段。

到目前爲止這麼好。

但是,只有當且僅當ClassA與ClassB至少有相同的字段(它可能有更多的字段)時,這纔會起作用。這是限制。如果ClassA和ClassB的不履行這個前提,你必須捕捉到的異常並分別處理這些案件,如:

try { 
    Field field = 
     instance.getClass().getDeclaredField(targetField.getName()); 
     field.setAccessible(true); 
     try { 
      targetField.set(target, field.get(instance)); 
     } catch (IllegalArgumentException e) { 
      handleIncompatibleType(target, targetField, field.get(instance)); 
     } 
    } catch (NoSuchFieldException e) { 
     handleMissingFieldInA(instance, targetField); 
    } 

相同的字段的意思是:相同的字段名稱和相同的字段類型(或至少「 castable'類型)

因此,如果您有List<ClassA>但需要List<ClassB>,您可以遍歷第一個列表,將List項目轉換爲ClassB對象並將它們寫入目標列表。

相關問題