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