I have two java objects as follows:
class A {
int a;
int b;
}
class B {
int a;
Double b;
}
A objA = new A();
objA.a = 5;
objA.b = 6;
I want to clone objA into objB such that field b gets converted to Double when accessed from objB i.e.
objB.b = 6.0
objB.a = 5
Note:
There are frameworks that do a mapping between objects of different classes. Chech out the comments.
If you don't want to use a third-party library, you could write an over-simplified version of what those frameworks offer.
For instance, if the names of the fields are identical, and the difference is only in types, we could write a method(A a, B b, Rules r)
which would map a
to b
by the given rules1:
public static void copyFromAtoB(A a, B b, Map<String, Function<Object, Object>> rules) throws NoSuchFieldException, IllegalAccessException {
for (Field f : B.class.getDeclaredFields()) {
final String fName = f.getName();
final Object aValue = A.class.getDeclaredField(f.getName()).get(a);
f.set(b, rules.containsKey(fName) ? rules.get(fName).apply(aValue) : aValue);
}
}
The rules2 tell what function we should apply to a field in order to set the value correctly.
If there is no rule for a field, we assume the types are compatible.
final A a = new A(5, 6);
final B b = new B();
final Map<String, Function<Object, Object>> rules = new HashMap<>();
rules.put("b", i -> Double.valueOf((int)i)); // int -> Double
copyFromAtoB(a, b, rules);
1 Yes, that's a reflection approach - it might be costly and over-engineered, but it seems pretty flexible.
2 Rules are not well-defined because we take an Object
and return an Object
(Function<Object, Object>
).
If you try to use Apache BeanUtils, as suggested in one of the comments, you'll see that it has a copyProperties
method, which can, to some degree, cast your types. For example, you can automatically get your double from an int, as it was in your example. However, if the properties really are not compatible, you'll get an exception and there seems to be no way to say you want to skip it.
My approach is to extend the BeanUtilsBean
class and add a method similar to copyProperties()
, but with an extra argument: a list of excepted properties:
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import org.apache.commons.beanutils.BeanUtilsBean;
public class MyBeanUtilsBean extends BeanUtilsBean {
public void copyPropertiesExcept(Object dest, Object orig, String... exceptProperties)
throws IllegalAccessException, InvocationTargetException {
PropertyDescriptor[] origDescriptors = getPropertyUtils().getPropertyDescriptors(orig);
for (int i = 0; i < origDescriptors.length; i++) {
String name = origDescriptors[i].getName();
if ("class".equals(name)) {
continue; // No point in trying to set an object's class
}
if (Arrays.asList(exceptProperties).contains(name)) {
continue;
}
if (getPropertyUtils().isReadable(orig, name) && getPropertyUtils().isWriteable(dest, name)) {
try {
Object value = getPropertyUtils().getSimpleProperty(orig, name);
copyProperty(dest, name, value);
} catch (NoSuchMethodException e) {
// Should not happen
}
}
}
}
}
Then you can use that method to copy all properties except the different ones:
import java.lang.reflect.InvocationTargetException;
public class B {
// ...
public static B fromA(A objA) {
B objB = new B();
// Copy common properties
try {
new MyBeanUtilsBean().copyPropertiesExcept(objB, objA, "d");
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
// Explicitly copy specific properties
objB.setD(new IntWrapper(objA.getD()));
return objB;
}
}
You can also try a complete working example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With