[java] 리플렉션을 통해 한 클래스의 필드에서 다른 클래스로 모든 값 복사

기본적으로 다른 클래스의 복사 본인 클래스가 있습니다.

public class A {
  int a;
  String b;
}

public class CopyA {
  int a;
  String b;
}

내가 뭐하는 거지하는 클래스의 값을 가하고 ACopyA전송하기 전에 CopyAWeb 서비스 호출을 통해. 이제 기본적으로 class에서 class 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();
            }

        }
    }

왠지 이미이 일을 한 사람이있을 것 같아요



답변

타사 라이브러리를 사용해도 괜찮다면 Apache Commons의 BeanUtilscopyProperties(Object, Object).


답변

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


답변

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


답변

다음은 작동하고 테스트 된 솔루션입니다. 클래스 계층 구조에서 매핑의 깊이를 제어 할 수 있습니다.

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


답변

내 솔루션 :

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객체 등의 클래스가 아닌 인스턴스에서, 그래서 당신은 일반적으로 그들에게 인스턴스를 전달해야 (정적 제외) 사용할 수 있습니다.


답변

도저

2012 년 11 월 19 일 업데이트 : 이제 새로운 ModelMapper 프로젝트 도 있습니다.