У меня есть класс, который по сути является копией другого класса.
public class A {
int a;
String b;
}
public class CopyA {
int a;
String b;
}
То , что я делаю, ввод значения из класса A
в CopyA
перед отправкой CopyA
через WebService вызова. Теперь я хотел бы создать метод отражения, который в основном копирует все идентичные (по имени и типу) поля из класса A
в класс CopyA
.
Как я могу это сделать?
Это то, что у меня есть, но это не совсем работает. Я думаю, что проблема здесь в том, что я пытаюсь установить поле на поле, которое я просматриваю.
private <T extends Object, Y extends Object> void copyFields(T from, Y too) {
Class<? extends Object> fromClass = from.getClass();
Field[] fromFields = fromClass.getDeclaredFields();
Class<? extends Object> tooClass = too.getClass();
Field[] tooFields = tooClass.getDeclaredFields();
if (fromFields != null && tooFields != null) {
for (Field tooF : tooFields) {
logger.debug("toofield name #0 and type #1", tooF.getName(), tooF.getType().toString());
try {
// Check if that fields exists in the other method
Field fromF = fromClass.getDeclaredField(tooF.getName());
if (fromF.getType().equals(tooF.getType())) {
tooF.set(tooF, fromF);
}
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Я уверен, что должен быть кто-то, кто уже как-то это сделал
java
reflection
Шервин Асгари
источник
источник
Ответы:
Если вы не против использования сторонней библиотеки, BeanUtils от Apache Commons справится с этим довольно легко, используя
copyProperties(Object, Object)
.источник
Почему бы вам не использовать библиотеку gson https://github.com/google/gson
вы просто конвертируете класс A в строку json. Затем преобразуйте jsonString в свой подкласс (CopyA). Используя приведенный ниже код:
Gson gson= new Gson(); String tmp = gson.toJson(a); CopyA myObject = gson.fromJson(tmp,CopyA.class);
источник
BeanUtils копирует только общедоступные поля и работает немного медленно. Вместо этого используйте методы получения и установки.
public Object loadData (RideHotelsService object_a) throws Exception{ Method[] gettersAndSetters = object_a.getClass().getMethods(); for (int i = 0; i < gettersAndSetters.length; i++) { String methodName = gettersAndSetters[i].getName(); try{ if(methodName.startsWith("get")){ this.getClass().getMethod(methodName.replaceFirst("get", "set") , gettersAndSetters[i].getReturnType() ).invoke(this, gettersAndSetters[i].invoke(object_a, null)); }else if(methodName.startsWith("is") ){ this.getClass().getMethod(methodName.replaceFirst("is", "set") , gettersAndSetters[i].getReturnType() ).invoke(this, gettersAndSetters[i].invoke(object_a, null)); } }catch (NoSuchMethodException e) { // TODO: handle exception }catch (IllegalArgumentException e) { // TODO: handle exception } } return null; }
источник
Вот рабочее и проверенное решение. Вы можете контролировать глубину отображения в иерархии классов.
public class FieldMapper { public static void copy(Object from, Object to) throws Exception { FieldMapper.copy(from, to, Object.class); } public static void copy(Object from, Object to, Class depth) throws Exception { Class fromClass = from.getClass(); Class toClass = to.getClass(); List<Field> fromFields = collectFields(fromClass, depth); List<Field> toFields = collectFields(toClass, depth); Field target; for (Field source : fromFields) { if ((target = findAndRemove(source, toFields)) != null) { target.set(to, source.get(from)); } } } private static List<Field> collectFields(Class c, Class depth) { List<Field> accessibleFields = new ArrayList<>(); do { int modifiers; for (Field field : c.getDeclaredFields()) { modifiers = field.getModifiers(); if (!Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) { accessibleFields.add(field); } } c = c.getSuperclass(); } while (c != null && c != depth); return accessibleFields; } private static Field findAndRemove(Field field, List<Field> fields) { Field actual; for (Iterator<Field> i = fields.iterator(); i.hasNext();) { actual = i.next(); if (field.getName().equals(actual.getName()) && field.getType().equals(actual.getType())) { i.remove(); return actual; } } return null; } }
источник
i.remove()
. Даже если вы создали итератор вы не можете вызватьremove
наList
«Siterator
. Должно бытьArrayList
Мое решение:
public static <T > void copyAllFields(T to, T from) { Class<T> clazz = (Class<T>) from.getClass(); // OR: // Class<T> clazz = (Class<T>) to.getClass(); List<Field> fields = getAllModelFields(clazz); if (fields != null) { for (Field field : fields) { try { field.setAccessible(true); field.set(to,field.get(from)); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } public static List<Field> getAllModelFields(Class aClass) { List<Field> fields = new ArrayList<>(); do { Collections.addAll(fields, aClass.getDeclaredFields()); aClass = aClass.getSuperclass(); } while (aClass != null); return fields; }
источник
Первым аргументом
tooF.set()
должен быть целевой объект (too
), а не поле, а вторым аргументом должно быть значение , а не поле, из которого оно получено. (Чтобы получить значение, вам нужно вызватьfromF.get()
- в данном случае снова передавая целевой объектfrom
.)Так работает большая часть API отражения. Вы получаете
Field
объекты,Method
объекты и т. Д. Из класса, а не из экземпляра, поэтому для их использования (кроме статики) вам обычно необходимо передать им экземпляр.источник
Бульдозер
ОБНОВЛЕНИЕ 19 ноября 2012 г .: Также появился новый проект ModelMapper .
источник
Это запоздалый пост, но он все еще может быть эффективен для людей в будущем.
Spring предоставляет служебную программу,
BeanUtils.copyProperties(srcObj, tarObj)
которая копирует значения из исходного объекта в целевой объект, когда имена переменных-членов обоих классов совпадают.Если есть преобразование даты (например, строка в дату) 'null' будет скопировано в целевой объект. Затем мы можем явно установить значения даты по мере необходимости.
BeanUtils from
Apache Common
выдает ошибку при несоответствии типов данных (особенно преобразование в дату и обратно)Надеюсь это поможет!
источник
Думаю, можно попробовать бульдозер . Он имеет хорошую поддержку преобразования bean-компонентов. Его также легко использовать. Вы можете либо ввести его в свое приложение Spring, либо добавить банку в путь к классу, и все готово.
Для примера вашего случая:
DozerMapper mapper = new DozerMapper(); A a= new A(); CopyA copyA = new CopyA(); a.set... // set fields of a. mapper.map(a,copyOfA); // will copy all fields from a to copyA
источник
Без использования BeanUtils или Apache Commons
public static <T1 extends Object, T2 extends Object> void copy(T1 origEntity, T2 destEntity) throws IllegalAccessException, NoSuchFieldException { Field[] fields = origEntity.getClass().getDeclaredFields(); for (Field field : fields){ origFields.set(destEntity, field.get(origEntity)); } }
источник
Spring имеет встроенный
BeanUtils.copyProperties
метод. Но это не работает с классами без геттеров / сеттеров. Еще одним вариантом копирования полей может быть сериализация / десериализация JSON. Для этого можно использовать Джексона. Если вы используете Spring. В большинстве случаев Джексон уже находится в вашем списке зависимостей.ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); Clazz copyObject = mapper.readValue(mapper.writeValueAsString(sourceObject), Clazz.class);
источник
Orika - это простая более быстрая структура сопоставления bean-компонентов, потому что она использует генерацию байтового кода. Он выполняет вложенные сопоставления и сопоставления с разными именами. Дополнительные сведения см. Здесь. Пример сопоставления может показаться сложным, но для сложных сценариев это будет просто.
MapperFactory factory = new DefaultMapperFactory.Builder().build(); mapperFactory.registerClassMap(mapperFactory.classMap(Book.class,BookDto.class).byDefault().toClassMap()); MapperFacade mapper = factory.getMapperFacade(); BookDto bookDto = mapperFacade.map(book, BookDto.class);
источник
SerializationUtils.clone()
собирается предоставить новый объект того же класса. Кроме того, он работает только с сериализуемыми классами.Если у вас есть Spring в зависимостях, вы также можете использовать org.springframework.beans.BeanUtils .
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html
источник
Я решил вышеуказанную проблему в Kotlin, которая отлично подходит для моей разработки приложений для Android:
object FieldMapper { fun <T:Any> copy(to: T, from: T) { try { val fromClass = from.javaClass val fromFields = getAllFields(fromClass) fromFields?.let { for (field in fromFields) { try { field.isAccessible = true field.set(to, field.get(from)) } catch (e: IllegalAccessException) { e.printStackTrace() } } } } catch (e: Exception) { e.printStackTrace() } } private fun getAllFields(paramClass: Class<*>): List<Field> { var theClass:Class<*>? = paramClass val fields = ArrayList<Field>() try { while (theClass != null) { Collections.addAll(fields, *theClass?.declaredFields) theClass = theClass?.superclass } }catch (e:Exception){ e.printStackTrace() } return fields }
}
источник
Из-за этого я не хотел добавлять зависимость к другому файлу JAR, поэтому написал что-то, что соответствовало бы моим потребностям. Я следую соглашению fjorm https://code.google.com/p/fjorm/, что означает, что мои общедоступные поля являются общедоступными и я не утруждаюсь писать сеттеры и геттеры. (на мой взгляд, код проще в управлении и на самом деле более читабелен)
Итак, я написал кое-что (на самом деле это не так уж сложно), что соответствует моим потребностям (предполагается, что у класса есть общедоступный конструктор без аргументов), и его можно извлечь в служебный класс
public Effect copyUsingReflection() { Constructor constructorToUse = null; for (Constructor constructor : this.getClass().getConstructors()) { if (constructor.getParameterTypes().length == 0) { constructorToUse = constructor; constructorToUse.setAccessible(true); } } if (constructorToUse != null) { try { Effect copyOfEffect = (Effect) constructorToUse.newInstance(); for (Field field : this.getClass().getFields()) { try { Object valueToCopy = field.get(this); //if it has field of the same type (Effect in this case), call the method to copy it recursively if (valueToCopy instanceof Effect) { valueToCopy = ((Effect) valueToCopy).copyUsingReflection(); } //TODO add here other special types of fields, like Maps, Lists, etc. field.set(copyOfEffect, valueToCopy); } catch (IllegalArgumentException | IllegalAccessException ex) { Logger.getLogger(Effect.class.getName()).log(Level.SEVERE, null, ex); } } return copyOfEffect; } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { Logger.getLogger(Effect.class.getName()).log(Level.SEVERE, null, ex); } } return null; }
источник
Основная идея Младена сработала (спасибо), но потребовалось несколько изменений, чтобы она была надежной, поэтому я внес их здесь.
Единственное место, где следует использовать этот тип решения, - это если вы хотите клонировать объект, но не можете, потому что это управляемый объект. Если вам посчастливилось иметь объекты, у которых есть 100% установщики без побочных эффектов для всех полей, вам определенно следует использовать вместо этого параметр BeanUtils.
Здесь я использую служебные методы lang3 для упрощения кода, поэтому, если вы вставляете его, вы должны сначала импортировать библиотеку Apache lang3.
Скопировать код
static public <X extends Object> X copy(X object, String... skipFields) { Constructor constructorToUse = null; for (Constructor constructor : object.getClass().getConstructors()) { if (constructor.getParameterTypes().length == 0) { constructorToUse = constructor; constructorToUse.setAccessible(true); break; } } if (constructorToUse == null) { throw new IllegalStateException(object + " must have a zero arg constructor in order to be copied"); } X copy; try { copy = (X) constructorToUse.newInstance(); for (Field field : FieldUtils.getAllFields(object.getClass())) { if (Modifier.isStatic(field.getModifiers())) { continue; } //Avoid the fields that you don't want to copy. Note, if you pass in "id", it will skip any field with "id" in it. So be careful. if (StringUtils.containsAny(field.getName(), skipFields)) { continue; } field.setAccessible(true); Object valueToCopy = field.get(object); //TODO add here other special types of fields, like Maps, Lists, etc. field.set(copy, valueToCopy); } } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new IllegalStateException("Could not copy " + object, e); } return copy; }
источник
public <T1 extends Object, T2 extends Object> void copy(T1 origEntity, T2 destEntity) { DozerBeanMapper mapper = new DozerBeanMapper(); mapper.map(origEntity,destEntity); } <dependency> <groupId>net.sf.dozer</groupId> <artifactId>dozer</artifactId> <version>5.4.0</version> </dependency>
источник
public static <T> void copyAvalableFields(@NotNull T source, @NotNull T target) throws IllegalAccessException { Field[] fields = source.getClass().getDeclaredFields(); for (Field field : fields) { if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) { field.set(target, field.get(source)); } } }
Читаем все поля класса. Отфильтруйте нестатические и незавершенные поля из результата. Но может быть ошибка доступа к закрытым полям. Например, если эта функция находится в том же классе, а копируемый класс не содержит общедоступных полей, произойдет ошибка доступа. Решением может быть размещение этой функции в том же пакете или изменение доступа к общедоступному или в этом коде внутри поля вызова цикла. SetAccessible (true); что сделает поля доступными
источник