Kopiowanie wszystkich wartości z pól w jednej klasie do drugiej poprzez odbicie

Mam klasę, która jest w zasadzie kopią innej klasy.

public class A {
  int a;
  String b;
}

public class CopyA {
  int a;
  String b;
}

To, co robię, to umieszczanie wartości z klasy A do CopyA przed wysłaniem CopyA przez wywołanie webservice. Teraz chciałbym stworzyć metodę reflection, która w zasadzie kopiuje wszystkie pola, które są identyczne (według nazwy i typu) z klasy A do klasy CopyA.

Jak mogę to zrobić? To jest to, co mam do tej pory, ale to nie do końca działa. Myślę, że problem polega na tym, że próbuję ustawić pole na polu, przez które przechodzę.
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();
            }

        }
    }

Jestem pewien, że musi być ktoś, kto już to jakoś zrobił

Author: Joe, 2009-11-03

15 answers

Jeśli nie masz nic przeciwko użyciu biblioteki innej firmy, BeanUtils z Apache Commons poradzi sobie z tym dość łatwo, używając copyProperties(Object, Object).

 71
Author: Greg Case,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-31 16:52:44

Dlaczego nie używasz biblioteki gson https://github.com/google/gson

Po prostu konwertujesz klasę A na łańcuch json. Następnie Konwertuj js na podklasę (CopyA) .użycie poniższego kodu:

Gson gson= new Gson();
String tmp = gson.toJson(a);
CopyA myObject = gson.fromJson(tmp,CopyA.class);
 15
Author: Eric Ho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-09 05:31:24

BeanUtils kopiuje tylko publiczne pola i jest nieco powolny. Zamiast tego stosuj metody getter i 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;
    }
 7
Author: Supun Sameera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-12 09:29:55

Pierwszym argumentem do tooF.set() powinien być Obiekt docelowy (too), a nie pole, a drugim argument powinien być wartość , a nie pole, z którego pochodzi wartość. (Aby uzyskać wartość, należy wywołać fromF.get() -- ponownie przekazując obiekt docelowy, w tym przypadku from.)

Większość API reflection działa w ten sposób. Otrzymujesz Field obiekty, Method obiekty itd. z klasy, a nie z instancji, więc aby z nich korzystać (z wyjątkiem statyki), zazwyczaj musisz je przekazać przykład.

 4
Author: David Moles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-03 15:14:57

Dozer

Aktualizacja 19 listopada 2012: jest teraz nowy projekt ModelMapper zbyt.

 4
Author: Ruben Bartelink,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-19 11:44:29
  1. Bez użycia BeanUtils lub Apache Commons

  2. 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));
         }
    }
    
 4
Author: Darkhan Iskakov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-19 13:36:27

Myślę, że możesz spróbować dozer . Ma dobre wsparcie dla konwersji fasoli na fasolę. Jest również łatwy w użyciu. Możesz albo wstrzyknąć go do aplikacji sprężynowej, albo dodać jar w ścieżce klasowej i gotowe.

Na przykład twojego przypadku:

 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
 3
Author: Priyank Doshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-17 10:40:42

Moje rozwiązanie:

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;
}
 3
Author: Mohsen Kashi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-20 14:10:01

Oto działające i sprawdzone rozwiązanie. Możesz kontrolować głębokość mapowania w hierarchii klas.

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;
    }
}
 3
Author: JHead,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-30 08:39:20

Tak lub BeanUtils z Apache Jakarta.

 1
Author: Shaun F,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-03 15:07:38

Orika jest prostym, szybszym frameworkiem mapowania bean, ponieważ generuje kod bajtowy. Robi zagnieżdżone mapowania i mapowania o różnych nazwach. Aby uzyskać więcej informacji, proszę Sprawdź tutaj Przykładowe mapowanie może wyglądać na złożone, ale w przypadku złożonych scenariuszy byłoby proste.

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);
 1
Author: Nagappan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-30 03:12:55

Jeśli masz spring w zależności możesz również użyć org.springframework.fasola.BeanUtils .

Https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html

 1
Author: db80,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-10 11:15:12

Sprężyna ma wbudowaną metodę BeanUtils.copyProperties. Ale to nie działa z klasami bez getterów/setterów. Kolejną opcją do kopiowania pól może być serializacja/deserializacja JSON. Jackson może być używany do tego celu. Jeśli używasz Springa w większości przypadków Jackson jest już na twojej liście zależności.

ObjectMapper mapper     = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Clazz        copyObject = mapper.readValue(mapper.writeValueAsString(sourceObject), Clazz.class);
 1
Author: Fırat KÜÇÜK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-28 20:46:09

Jest to późny post, ale nadal może być skuteczny dla ludzi w przyszłości.

Spring dostarcza narzędzie BeanUtils.copyProperties(srcObj, tarObj), które kopiuje wartości z obiektu źródłowego do obiektu docelowego, gdy nazwy zmiennych składowych obu klas są takie same.

Jeśli istnieje konwersja daty, (np. Łańcuch do daty) 'null' zostanie skopiowany do obiektu docelowego. Możemy wtedy jawnie ustawić wartości daty zgodnie z wymaganiami.

BeanUtils z Apache Common rzuca błąd, gdy występuje niedopasowanie z typów danych (esp. konwersja do I Od Daty)

Mam nadzieję, że to pomoże!
 1
Author: Nicholas K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-06 14:04:38

Nie chciałem z tego powodu dodawać zależności do innego pliku JAR, więc napisałem coś, co odpowiadałoby moim potrzebom. I follow the convention of fjorm https://code.google.com/p/fjorm / co oznacza, że moje ogólnie dostępne pola są publiczne i że nie zawracam sobie głowy pisaniem setterów i getterów. (moim zdaniem kod jest łatwiejszy do zarządzania i bardziej czytelny)

Więc napisałem coś (nie jest to właściwie trudne), co odpowiada moim potrzebom (zakłada, że klasa ma public constructor bez args) i może być wyodrębniony do klasy użytkowej

  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;
  }
 0
Author: Mladen Adamovic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-04 18:28:07