将列表值反映到另一个列表中?

时间:2010-01-08 07:17:31

标签: java reflection

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;
}

3 个答案:

答案 0 :(得分:3)

一般情况下,convert方法不太适用(适用于List或任何其他类型)。

调用Field.setAccessible(true)允许对私有字段进行读写访问,但不允许通过final修改Field.set()字段(抛出IllegalAccessException: Field is 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,此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);

答案 1 :(得分:1)

试试吧。

但不需要它。您只需将第一个列表的内容添加到第二个列表中即可:

List list2 = new ArrayList(list1);

看起来,你的两个List声明了不同的泛型类型。所以你将不得不使用不安全的演员阵容。但这样做感觉非常糟糕。你究竟想要实现什么目标?如果列表中的对象类型应该不同,那么为什么要尝试复制它们呢?

答案 2 :(得分:0)

技术上说,是的,但仅限于某些条件。假设您要将ClassA实例转换为ClassB实例。开始转换的代码行如下:

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

到目前为止我是否正确?

上面的代码将(1)创建一个新的ClassB对象,使用它的默认构造函数,(2)获取在ClassB上声明的所有字段(但没有字段来自任何ClassB超类型!),(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);
    }

相同字段表示:相同的字段名称和相同的字段类型(或至少是“可投射”类型)

因此,如果您有List<ClassA>但需要List<ClassB>,则可以遍历第一个列表,将List项目转换为ClassB对象并将它们写入目标列表。