2010-01-08 83 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对象并将它们写入目标列表。

相关问题