Ich habe eine Klasse, die im Grunde eine Kopie einer anderen Klasse ist.
public class A {
int a;
String b;
}
public class CopyA {
int a;
String b;
}
Was ich tue , ist , Werte aus der Klasse setzen A
in CopyA
vor dem Senden CopyA
über einen Webservice Aufruf. Jetzt möchte ich eine Reflexionsmethode erstellen, die grundsätzlich alle Felder kopiert, die (nach Name und Typ) von Klasse A
zu Klasse identisch sind CopyA
.
Wie kann ich das machen?
Das habe ich bisher, aber es funktioniert nicht ganz. Ich denke, das Problem hier ist, dass ich versuche, ein Feld auf dem Feld zu setzen, das ich durchlaufe.
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();
}
}
}
Ich bin sicher, es muss jemanden geben, der das schon irgendwie gemacht hat
java
reflection
Shervin Asgari
quelle
quelle
Antworten:
Wenn es Ihnen nichts ausmacht, eine Bibliothek eines Drittanbieters zu verwenden, können BeanUtils von Apache Commons dies ganz einfach verwenden
copyProperties(Object, Object)
.quelle
Warum verwenden Sie nicht die gson-Bibliothek https://github.com/google/gson ?
Sie konvertieren einfach die Klasse A in einen JSON-String. Konvertieren Sie dann jsonString in Ihre Unterklasse (CopyA). Verwenden Sie dazu den folgenden Code:
Gson gson= new Gson(); String tmp = gson.toJson(a); CopyA myObject = gson.fromJson(tmp,CopyA.class);
quelle
BeanUtils kopiert nur öffentliche Felder und ist etwas langsam. Gehen Sie stattdessen mit Getter- und Setter-Methoden.
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; }
quelle
Hier ist eine funktionierende und getestete Lösung. Sie können die Tiefe der Zuordnung in der Klassenhierarchie steuern.
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; } }
quelle
i.remove()
. Auch wenn Sie Iterator erstellt haben , können Sie nicht rufen Sieremove
aufList
‚siterator
. Es sollte seinArrayList
Meine Lösung:
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; }
quelle
Das erste Argument
tooF.set()
sollte das Zielobjekt (too
) sein, nicht das Feld, und das zweite Argument sollte der Wert sein , nicht das Feld, aus dem der Wert stammt. (Um den Wert zu erhalten, müssen Sie aufrufenfromF.get()
und in diesem Fall erneut ein Zielobjekt übergebenfrom
.)Die meisten Reflection-APIs funktionieren auf diese Weise. Sie erhalten
Field
Objekte,Method
Objekte usw. von der Klasse, nicht von einer Instanz. Um sie zu verwenden (mit Ausnahme der Statik), müssen Sie ihnen im Allgemeinen eine Instanz übergeben.quelle
Bulldozer
UPDATE 19. November 2012: Es gibt jetzt auch ein neues ModelMapper-Projekt .
quelle
Dies ist ein später Beitrag, kann aber auch in Zukunft für Menschen wirksam sein.
Spring bietet ein Dienstprogramm,
BeanUtils.copyProperties(srcObj, tarObj)
das Werte vom Quellobjekt zum Zielobjekt kopiert, wenn die Namen der Elementvariablen beider Klassen identisch sind.Bei einer Datumskonvertierung (z. B. String to Date) wird 'null' in das Zielobjekt kopiert. Wir können dann die Werte des Datums explizit nach Bedarf einstellen.
Die BeanUtils von geben
Apache Common
einen Fehler aus, wenn die Datentypen nicht übereinstimmen (insbesondere die Konvertierung von und nach Datum).Hoffe das hilft!
quelle
Ich denke, Sie können Bulldozer versuchen . Es bietet eine gute Unterstützung für die Umwandlung von Bohnen in Bohnen. Es ist auch einfach zu bedienen. Sie können es entweder in Ihre Federanwendung injizieren oder das Glas in den Klassenpfad einfügen und fertig.
Ein Beispiel für Ihren Fall:
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
quelle
Ohne BeanUtils oder 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)); } }
quelle
Der Frühling hat eine eingebaute
BeanUtils.copyProperties
Methode. Aber es funktioniert nicht mit Klassen ohne Getter / Setter. JSON-Serialisierung / Deserialisierung kann eine weitere Option zum Kopieren von Feldern sein. Jackson kann für diesen Zweck verwendet werden. Wenn Sie Spring verwenden In den meisten Fällen befindet sich Jackson bereits in Ihrer Abhängigkeitsliste.ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); Clazz copyObject = mapper.readValue(mapper.writeValueAsString(sourceObject), Clazz.class);
quelle
Orika's ist ein einfaches, schnelleres Bean-Mapping-Framework, da es durch Bytecode-Generierung funktioniert. Es werden verschachtelte Zuordnungen und Zuordnungen mit unterschiedlichen Namen ausgeführt. Weitere Informationen finden Sie hier. Die Beispielzuordnung mag komplex aussehen, bei komplexen Szenarien wäre sie jedoch einfach.
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);
quelle
SerializationUtils.clone()
wird ein neues Objekt der gleichen Klasse geben. Außerdem funktioniert es nur mit serialisierbaren Klassen.Wenn Sie Spring in den Abhängigkeiten haben, können Sie auch org.springframework.beans.BeanUtils verwenden .
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html
quelle
Ich habe das oben genannte Problem in Kotlin gelöst, das für meine Android Apps-Entwicklung gut funktioniert:
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 }
}}
quelle
Aus diesem Grund wollte ich keine Abhängigkeit zu einer anderen JAR-Datei hinzufügen, also habe ich etwas geschrieben, das meinen Anforderungen entspricht. Ich folge der Konvention von fjorm https://code.google.com/p/fjorm/ was bedeutet, dass meine allgemein zugänglichen Felder öffentlich sind und ich mir nicht die Mühe mache, Setter und Getter zu schreiben. (Meiner Meinung nach ist der Code einfacher zu verwalten und tatsächlich besser lesbar.)
Also habe ich etwas geschrieben (es ist eigentlich nicht sehr schwierig), das meinen Anforderungen entspricht (vorausgesetzt, die Klasse hat einen öffentlichen Konstruktor ohne Argumente) und es könnte in eine Utility-Klasse extrahiert werden
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; }
quelle
Die Grundidee von Mladen hat funktioniert (danke), aber es waren einige Änderungen erforderlich, um robust zu sein. Deshalb habe ich sie hier beigesteuert.
Der einzige Ort, an dem diese Art von Lösung verwendet werden sollte, ist, wenn Sie das Objekt klonen möchten, dies jedoch nicht können, da es sich um ein verwaltetes Objekt handelt. Wenn Sie das Glück haben, Objekte zu haben, die alle 100% nebenwirkungsfreie Setter für alle Felder haben, sollten Sie stattdessen auf jeden Fall die Option BeanUtils verwenden.
Hier verwende ich die Dienstprogrammmethoden von lang3, um den Code zu vereinfachen. Wenn Sie ihn einfügen, müssen Sie zuerst die lang3-Bibliothek von Apache importieren.
Code kopieren
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; }
quelle
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>
quelle
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)); } } }
Wir lesen alle Felder der Klasse. Filtern Sie nicht statische und nicht endgültige Felder aus dem Ergebnis. Es kann jedoch ein Fehler beim Zugriff auf nicht öffentliche Felder auftreten. Wenn sich diese Funktion beispielsweise in derselben Klasse befindet und die zu kopierende Klasse keine öffentlichen Felder enthält, tritt ein Zugriffsfehler auf. Die Lösung kann darin bestehen, diese Funktion im selben Paket zu platzieren oder den Zugriff auf public oder in diesem Code innerhalb des Schleifenaufruffelds zu ändern. SetAccessible (true); Was macht die Felder verfügbar
quelle