簡體   English   中英

java中如何合並兩個復雜的對象

[英]How to merge two complex objects in java

我有兩個 java 對象,我想將它們合並為單個對象。 問題是這兩個對象不包含普通的原始類型屬性(字段),它們包含復雜類型的屬性(如其他類型的對象和其他類型的對象列表)。

對象 1:通過設置一些屬性(字段)和返回

對象 2:通過設置一些屬性(字段)返回,或者它可能返回它所擁有但不是對象 1 返回的類型的新對象。

對象 1 和對象 2 的類型相同。

結果對象 3 = obj1 屬性 + 如果與 obj1 的類型相同,則更新來自 obj 2 的屬性 + 來自 obj2 的新更新對象

使用 spring 提供的org.springframework.beans.BeanUtils類很容易做到。 或者我認為 Springs 版本基於或相同的Apache Commons BeanUtils 庫

public static <T> T combine2Objects(T a, T b) throws InstantiationException, IllegalAccessException {
    // would require a noargs constructor for the class, maybe you have a different way to create the result.
    T result = (T) a.getClass().newInstance();
    BeanUtils.copyProperties(a, result);
    BeanUtils.copyProperties(b, result);
    return result;
}

如果你不能或沒有 noargs 構造函數,也許你只是傳入結果

public static <T> T combine2Objects(T a, T b, T destination) {
    BeanUtils.copyProperties(a, destination);
    BeanUtils.copyProperties(b, destination);
    return destination;
}

如果你不想復制空屬性,你可以使用這樣的東西:

public static void nullAwareBeanCopy(Object dest, Object source) throws IllegalAccessException, InvocationTargetException {
    new BeanUtilsBean() {
        @Override
        public void copyProperty(Object dest, String name, Object value)
                throws IllegalAccessException, InvocationTargetException {
            if(value != null) {
                super.copyProperty(dest, name, value);
            }
        }
    }.copyProperties(dest, source);
}

嵌套對象解決方案

這是一個更強大的解決方案。 支持嵌套對象復制,1+級深度的對象將不再通過引用復制,而是復制嵌套對象或單獨復制它們的屬性。

/**
 * Copies all properties from sources to destination, does not copy null values and any nested objects will attempted to be
 * either cloned or copied into the existing object. This is recursive. Should not cause any infinite recursion.
 * @param dest object to copy props into (will mutate)
 * @param sources
 * @param <T> dest
 * @return
 * @throws IllegalAccessException
 * @throws InvocationTargetException
 */
public static <T> T copyProperties(T dest, Object... sources) throws IllegalAccessException, InvocationTargetException {
    // to keep from any chance infinite recursion lets limit each object to 1 instance at a time in the stack
    final List<Object> lookingAt = new ArrayList<>();

    BeanUtilsBean recursiveBeanUtils = new BeanUtilsBean() {

        /**
         * Check if the class name is an internal one
         * @param name
         * @return
         */
        private boolean isInternal(String name) {
            return name.startsWith("java.") || name.startsWith("javax.")
                    || name.startsWith("com.sun.") || name.startsWith("javax.")
                    || name.startsWith("oracle.");
        }

        /**
         * Override to ensure that we dont end up in infinite recursion
         * @param dest
         * @param orig
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         */
        @Override
        public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
            try {
                // if we have an object in our list, that means we hit some sort of recursion, stop here.
                if(lookingAt.stream().anyMatch(o->o == dest)) {
                    return; // recursion detected
                }
                lookingAt.add(dest);
                super.copyProperties(dest, orig);
            } finally {
                lookingAt.remove(dest);
            }
        }

        @Override
        public void copyProperty(Object dest, String name, Object value)
                throws IllegalAccessException, InvocationTargetException {
            // dont copy over null values
            if (value != null) {
                // attempt to check if the value is a pojo we can clone using nested calls
                if(!value.getClass().isPrimitive() && !value.getClass().isSynthetic() && !isInternal(value.getClass().getName())) {
                    try {
                        Object prop = super.getPropertyUtils().getProperty(dest, name);
                        // get current value, if its null then clone the value and set that to the value
                        if(prop == null) {
                            super.setProperty(dest, name, super.cloneBean(value));
                        } else {
                            // get the destination value and then recursively call
                            copyProperties(prop, value);
                        }
                    } catch (NoSuchMethodException e) {
                        return;
                    } catch (InstantiationException e) {
                        throw new RuntimeException("Nested property could not be cloned.", e);
                    }
                } else {
                    super.copyProperty(dest, name, value);
                }
            }
        }
    };


    for(Object source : sources) {
        recursiveBeanUtils.copyProperties(dest, source);
    }

    return dest;
}

它有點快速和骯臟,但效果很好。 由於它確實使用了遞歸,並且存在無限遞歸的潛力,因此我確實將其置於安全位置。

下面的方法將忽略 serialVersionUID,遍歷所有字段並從對象 a --> 對象 b 復制非空值,如果它們在 b 中為空。 換句話說,如果 b 中的任何字段為空,則從 a 中取出它,如果它不為空。

public static <T> T combine2Objects(T a, T b) throws InstantiationException,IllegalAccessException{
            T result = (T) a.getClass().newInstance();
            Object[] fields = Arrays.stream(a.getClass().getDeclaredFields()).filter(f -> !f.getName().equals("serialVersionUID")).collect(Collectors.toList()).toArray();
            for (Object fieldobj : fields) {
                Field field = (Field) fieldobj;
                field.set(result, field.get(b) != null ? field.get(b) : field.get(a));
            }
            return result;
    }

嘗試使用class.getFields

    Field[] fields = YourClass.getFields();
    for (Field field : fields) {
         // get value
         YourObject value = field.get(objectInstance);
         // check the values are different, then update 
         field.set(objetInstance, value);    
    }

嘗試這個

public <T> T objectMerge(T local, T remote, boolean toappend) throws Exception {
    Class<?> clazz = local.getClass();
    Object merged = clazz.newInstance();
    for (Field field : clazz.getDeclaredFields()) {
        field.setAccessible(true);
        Object localValue = field.get(local);
        Object remoteValue = field.get(remote);
        if (localValue != null) {
            String key = "";
            if (localValue.getClass().getSimpleName().toLowerCase().contains("map")) {
                key = "map";
            } else if (localValue.getClass().getSimpleName().toLowerCase().contains("set")) {
                key = "set";
            } else if (localValue.getClass().getSimpleName().toLowerCase().contains("list")) {
                key = "list";
            } else {
                key = localValue.getClass().getSimpleName();
            }
            switch (key) {
                case "Default":
                case "Detail":
                case "String":
                case "Date":
                case "Integer":
                case "Float":
                case "Long":
                case "Double":
                case "Object":
                    field.set(merged, (remoteValue != null) ? remoteValue : localValue);
                    break;
                case "map":
                    if (toappend) {
                        ((Map) localValue).putAll((Map) remoteValue);
                    } else {
                        localValue = (remoteValue != null) ? remoteValue : localValue;
                    }
                    field.set(merged, localValue);
                    break;
                case "list":
                    if (toappend) {
                        ((List) localValue).addAll((List) remoteValue);
                    } else {
                        localValue = (remoteValue != null) ? remoteValue : localValue;
                    }
                    field.set(merged, localValue);
                    break;
                case "set":
                    if (toappend) {
                        ((Set) localValue).addAll((Set) remoteValue);
                    } else {
                        localValue = (remoteValue != null) ? remoteValue : localValue;
                    }
                    field.set(merged, localValue);
                    break;
                default:
                    field.set(merged, this.objectMerge(localValue, remoteValue, toappend));
                    break;
            }
        }
    }
    return (T) merged;
}
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;

public final class PropertyMerger {

    public static <T> void mergeProperty(
            Supplier<T> sourceGetter,
            Supplier<T> targetGetter,
            Consumer<T> targetSetter
    ) {
        var source = sourceGetter.get();
        var target = targetGetter.get();

        if (!Objects.equals(source, target)) {
            targetSetter.accept(source);
        }
    }

}

在您的代碼中的某處:

PropertyMerger.mergeProperty(facebookOAuth2User::getId, existingFacebookOAuth2UserDB::getFacebookId, existingFacebookOAuth2UserDB::setFacebookId);
PropertyMerger.mergeProperty(facebookOAuth2User::getName, existingFacebookOAuth2UserDB::getName, existingFacebookOAuth2UserDB::setName);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM