我有一个类,它基本上是另一个类的副本。
public class A {
int a;
String b;
}
public class CopyA {
int a;
String b;
}
我正在做的是在通过网络服务调用发送A
之前,将CopyA
类中的值放入CopyA
。现在我想创建一个反射方法,它基本上将所有相同的字段(按名称和类型)从类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();
}
}
}
我相信必定有人已经以某种方式完成了这件事
答案 0 :(得分:78)
如果您不介意使用第三方库,来自Apache Commons的BeanUtils将使用copyProperties(Object, Object)
轻松处理此问题。
答案 1 :(得分:17)
为什么不使用gson库https://github.com/google/gson
您只需将A类转换为json字符串即可。然后将jsonString转换为subClass(CopyA)。使用下面的代码:
Gson gson= new Gson();
String tmp = gson.toJson(a);
CopyA myObject = gson.fromJson(tmp,CopyA.class);
答案 2 :(得分:8)
BeanUtils只会复制公共字段,而且速度有点慢。而是使用getter和setter方法。
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;
}
答案 3 :(得分:4)
更新2012年11月19日:现在还有new ModelMapper project。
答案 4 :(得分:4)
tooF.set()
的第一个参数应该是目标对象(too
),而不是字段,第二个参数应该是值,而不是字段值来自。 (要获取值,您需要调用fromF.get()
- 再次传入目标对象,在本例中为from
。)
大多数反射API都以这种方式工作。您从类中获取Field
个对象,Method
个对象,而不是从实例获取,因此要使用它们(静态除外),您通常需要向它们传递一个实例。
答案 5 :(得分:4)
不使用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));
}
}
答案 6 :(得分:3)
我想你可以试试dozer。它对bean到bean的转换有很好的支持。 它也易于使用。您可以将它注入到spring应用程序中,也可以在类路径中添加jar并完成它。
以您的案例为例:
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
答案 7 :(得分:3)
这是一个经过实践检验的解决方案。您可以在类层次结构中控制映射的深度。
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;
}
}
答案 8 :(得分:3)
我的解决方案:
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;
}
答案 9 :(得分:2)
Spring有一个内置的BeanUtils.copyProperties
方法。但它不适用于没有getter / setter的类。 JSON序列化/反序列化可以是复制字段的另一种选择。杰克逊可以用于此目的。如果您使用Spring在大多数情况下,Jackson已经在您的依赖列表中。
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Clazz copyObject = mapper.readValue(mapper.writeValueAsString(sourceObject), Clazz.class);
答案 10 :(得分:1)
是的,还是来自Apache Jakarta的BeanUtils。
答案 11 :(得分:1)
我在Kotlin中解决了上述问题,该问题对我的Android Apps开发非常适用:
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
}
}
答案 12 :(得分:1)
这是一个迟到的帖子,但对未来的人们仍然有效。
Spring提供了一个实用程序BeanUtils.copyProperties(srcObj, tarObj)
,当两个类的成员变量的名称相同时,它会将值从源对象复制到目标对象。
如果存在日期转换(例如,String to Date),则将'null'复制到目标对象。然后,我们可以根据需要明确设置日期的值。
来自Apache Common
的BeanUtils在数据类型不匹配时引发错误(特别是与日期的转换)
希望这有帮助!
答案 13 :(得分:1)
如果依赖项中有spring,则还可以使用 org.springframework.beans.BeanUtils 。
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html
答案 14 :(得分:1)
Orika是简单快速的bean映射框架,因为它通过字节代码生成。它使用不同的名称嵌套映射和映射。有关详细信息,请check here 样本映射可能看起来很复杂,但对于复杂的场景,它很简单。
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);
答案 15 :(得分:0)
我不想因为这个而将依赖项添加到另一个JAR文件中,所以写了一些适合我需要的东西。我遵循fjorm https://code.google.com/p/fjorm/的惯例,这意味着我普遍可访问的字段是公共的,我不打算写setter和getter。 (在我看来,代码更容易管理,实际上更具可读性)
所以我写了一些东西(实际上并不困难),它符合我的需要(假设该类有没有args的公共构造函数),它可以被提取到实用程序类中
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;
}
答案 16 :(得分:0)
Mladen的基本想法奏效了(谢谢),但是需要进行一些更改才能使其健壮,因此我在此处做出了贡献。
唯一要使用这种解决方案的地方是您要克隆对象,但是不能,因为它是一个托管对象。如果您很幸运地拥有所有字段都具有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;
}
答案 17 :(得分:0)
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>
答案 18 :(得分:0)
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));
}
}
}
我们阅读了课程的所有字段。从结果中过滤非静态和非最终字段。 但是访问非公共字段可能会出错。 例如,如果此函数在同一类中,并且要复制的类不包含公共字段,则将发生访问错误。解决方案可能是将此函数放在同一程序包中,或者在循环调用内更改对public或此代码的访问 field.setAccessible(true); 什么将使这些字段可用