简体   繁体   中英

Copy whitelist of attributes from class one instance to the other

I'd like to know if it's possible to copy a list of attributes from one instance to the other. Ideally I'd like this to be done in a typesafe way, without using attributes as strings but rather being able to generate accessor handles at build time.

For example, usage should look like:

MyJavaBean sourceBean = new MyJavaBean();
sourceBean.setX("x val");
sourceBean.setY("y val");

MyJavaBean targetBean = new MyJavaBean();

copyAttributes(sourceBean,targetBean,Arrays.asList(MyJavaBean._field_x));

And only sourceBean.x would be copied to targetBean.x

My Java is a bit rusty, I'm sure I've already seen that somewhere. I've looked at bit at Lombok but it does not seem to permit this.

Does anyone have any hint on how to achieve that?

I must say that my app will have to maintain at least 50 whitelists of this kind, for a complex migration operation, so manually building copy methods or putting thousands of annotations on fields would be a pain, and type safety is important for me.

I'm NOT looking for: - How to copy ALL properties from one object to another - How to do a deep clone using Serializable

I know that you could use some library like BeanUtils .

But in this specific case could you not just use plain Java libraries and create a method like this one:

static <T> void copyAttributes(T source, T target, List<Field> fieldList) throws IllegalAccessException {
    for (Field f : fieldList) {
        f.setAccessible(true);
        f.set(target, f.get(source));
    }
}

Here is a running toy example which you can execute:

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;

class JavaBean {

    private String X;

    public String getX() {
        return X;
    }

    public void setX(String x) {
        X = x;
    }

    @Override
    public String toString() {
        return "{" +
                "X='" + X + '\'' +
                '}';
    }
}

public class MyJavaBeanTest {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        JavaBean a = new JavaBean();
        a.setX("x");
        JavaBean b = new JavaBean();
        copyAttributes(a, b, Arrays.asList(a.getClass().getDeclaredField("X")));
        System.out.println(b);
    }

    static <T> void copyAttributes(T source, T target, List<Field> fieldList) throws IllegalAccessException {
        for (Field f : fieldList) {
            f.setAccessible(true);
            f.set(target, f.get(source));
        }
    }
}

What is the problem with this simplistic approach for this use case?

Use the Introspector / BeanInfo API:

public static <T> void copyAttributes(T source, T target, Set<String> whiteList) {
    try {
        Arrays.stream(
            Introspector.getBeanInfo(source.getClass(), Object.class).getPropertyDescriptors())
              .filter(d -> whiteList.contains(d.getName()))
              .filter(d -> d.getReadMethod() != null)
              .filter(d -> d.getWriteMethod() != null)
              .forEach(d -> copyProperty(source, target, d));
    } catch (IntrospectionException e) {
        throw new IllegalStateException(e);
    }
}

private static <T> void copyProperty(final T source, final T target, final PropertyDescriptor d) {
    try {
        Object value = d.getReadMethod().invoke(source);
        d.getWriteMethod().invoke(target, value);
    } catch (IllegalAccessException | InvocationTargetException e) {
        throw new IllegalStateException(e);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM