Отразить значение списка в другом списке? - PullRequest
2 голосов
/ 08 января 2010
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 ]

3 голосов
/ 08 января 2010

В общем случае метод 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 serialVersionUID в 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; 
}

Однако следующая проблема заключается в том, что метод convert выполняет поверхностное копирование. Для List s различных типов эта измененная версия convert может работать правильно, но она не преобразует объекты ClassA в списке в ClassB (непроверенный приведенный выше код скрывает это). Это может привести к тому, что ClassCastException s будет добавлено позже в приложение.

Исправить эту проблему можно, добавив еще один метод для переноса 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 голос
/ 08 января 2010

Попробуй.

Но это не нужно. Вы можете просто добавить содержимое первого списка во второй:

List list2 = new ArrayList(list1);

Как кажется, ваши два List объявлены с различными универсальными типами. Так что вам придется использовать небезопасные приведения. Но это ужасно неправильно. Чего вы на самом деле пытаетесь достичь? Если типы объектов в списках должны быть разными, то почему вы пытаетесь их скопировать?

0 голосов
/ 08 января 2010

Технически, да, но только при определенных условиях. Предположим, вы хотите преобразовать экземпляр 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);
    }

Одинаковые поля означают: одно и то же имя поля и тот же тип поля (или, по крайней мере, тип 'castable')

Так что, если у вас есть List<ClassA>, но вам нужен List<ClassB>, вы можете перебрать первый список, преобразовать элементы списка в объекты ClassB и записать их в целевой список.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...