views:

107

answers:

3
List <ClassA> listA; List <ClassB> listB;

Can I use reflection to reflect listA into listB? I got below code but only reflect an object

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;
}
+1  A: 

Try it.

But it is not needed. You can just add the contents of the first list to the second one:

List list2 = new ArrayList(list1);

As it seems, your two Lists are declared with different generic types. So you will have to use unsafe casts. But it feels horribly wrong to do this. What are you actually trying to achieve? If the types of objects in the lists are supposed to be different, then why are you trying to copy them?

Bozho
Sorry i didn't notive my <ClassA> <ClassB> doesn't show out. Basically is two List that take in different class type of object
actually this two Class have same attributes, for some reason must convert the ClassB into ClassA
then use the exampel I gave you. If the compiler complains, use casts
Bozho
Ya this method I tried before already, beside using this is it possible to use reflection?
you need not do it with reflection. You can, if you want. Check your other question for details.
Bozho
+2  A: 

In general the convert method is not likely to work (for Lists or any other type).

Calling Field.setAccessible(true) allows read and write access to private fields but will not allow modification of final fields via Field.set() (an IllegalAccessException: Field is final exception is thrown).

Depending on the implementation of List you are trying to copy this may prevent it from working correctly. For example, using ArrayList such as:

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

fails when trying to copy serialVersionUID.

The following change to the posted code gets round this problem for static final serialVersionUID in ArrayList:

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

However, the next problem is that the convert method is performing a shallow copy. For Lists of different types this altered version of convert may appear to work correctly but it does not convert the ClassA objects in the list to ClassB (the unchecked cast above hides this). This will likely cause ClassCastExceptions to be thrown later in the application.

Fixing this problem can be achieved by adding another method to wrap 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;
}

This will then need to be called as:

List<ClassB> listB = (List<ClassB>) convertList(
    listA, ArrayList.class, ClassB.class);
Matthew Murdoch
Hi Matthew Murdoch can you explain to me why will cause fails when trying to copy serialVersionUID when reflect the list and one object to object wont get this error. And what you mean by shallow copy? Thanks you veyr much
A: 

Technically spoken, yes, but only under certain conditions. Assume, you want to convert a ClassA instance into a ClassB instance. The line of code to start the conversion would be like:

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

Am I correct so far?

The above code will (1) create a new ClassB object, using it's default constructor, (2) get all fields declared on ClassB (but no fields from any ClassB supertype!), (3) get the same field from ClassA and (4) set the value from aInstance to the field in the newly created ClassB instance.

So far so good.

But this will only work if and only if ClassA has exact the same fields as ClassB at minimum (it may have more fields). That's the limitation. If ClassA and ClassB do not fulfill this precondition, you'll have to catch the exceptions and handle those cases separately, like:

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

Same fields means: same field name and same field type (or at least a 'castable' type)

So if you have a List<ClassA> but need a List<ClassB>, you could iterate through the first list, convert the List items into ClassB objects and write them to the target list.

Andreas_D