通过直接字段访问复制对象属性 [英] Copy object properties by direct field access

查看:44
本文介绍了通过直接字段访问复制对象属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有一种简单的方法可以将对象的属性复制到具有相同字段名称的其他类的另一个对象中,而该对象具有相同的字段名称使用直接字段访问-即,当其中一个类没有getter或setter时田间?当它们都具有getter和setter方法时,我可以使用 org.springframework.beans.BeanUtils#copyProperties(Object source,Object target),但是当它们没有getter和setter方法时,我该怎么办?

Is there an easy way to copy an object's property's onto another object of a different class which has the same field names using direct field access - i.e. when one of the classes does not have getters or setters for the fields? I can use org.springframework.beans.BeanUtils#copyProperties(Object source, Object target) when they both have getter and setter methods, but what can I do when they don't?

这些字段是公开的也可能是相关的.

It may also be relevant that the fields are public.

我知道我可以编写自己的代码来使用反射来做到这一点,但我希望有一些库可以提供单行代码.

I know that I can write my own code to do this using reflection, but I'm hoping that there's some library that provides a one-liner.

推荐答案

我没有找到第三方库来完全按照我的意愿进行此操作.我会将代码粘贴在这里,以防对任何人有用:

I didn't find a 3rd-party library to do this quite how I wanted. I'll paste my code here in case it is useful to anyone:

import java.lang.reflect.Field;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An alternative to Spring's BeanUtils#copyProperties for classes that don't have getters and setters.
 */
public class FieldCopier {

    private static final Logger log = LoggerFactory.getLogger(FieldCopier.class);

    /** Always use the same instance, so that we can cache the fields. */
    private static final FieldCopier instance = new FieldCopier();

    /** Caching the paired fields cuts the time taken by about 25% */
    private final Map<Map.Entry<Class<?>, Class<?>>, Map<Field, Field>> PAIRED_FIELDS = new ConcurrentHashMap<>();
    /** Caching the fields cuts the time taken by about 50% */
    private final Map<Class<?>, Field[]> FIELDS = new ConcurrentHashMap<>();

    public static FieldCopier instance() {
        return instance;
    }

    private FieldCopier() {
        // do not instantiate
    }

    public <S, T> T copyFields(S source, T target) {
        Map<Field, Field> pairedFields = getPairedFields(source, target);
        for (Field sourceField : pairedFields.keySet()) {
            Field targetField = pairedFields.get(sourceField);
            try {
                Object value = getValue(source, sourceField);
                setValue(target, targetField, value);
            } catch(Throwable t) {
                throw new RuntimeException("Failed to copy field value", t);
            }
        }
        return target;
    }

    private <S, T> Map<Field, Field> getPairedFields(S source, T target) {
        Class<?> sourceClass = source.getClass();
        Class<?> targetClass = target.getClass();
        Map.Entry<Class<?>, Class<?>> sourceToTarget = new AbstractMap.SimpleImmutableEntry<>(sourceClass, targetClass);
        PAIRED_FIELDS.computeIfAbsent(sourceToTarget, st -> mapSourceFieldsToTargetFields(sourceClass, targetClass));
        Map<Field, Field> pairedFields = PAIRED_FIELDS.get(sourceToTarget);
        return pairedFields;
    }

    private Map<Field, Field> mapSourceFieldsToTargetFields(Class<?> sourceClass, Class<?> targetClass) {
        Map<Field, Field> sourceFieldsToTargetFields = new HashMap<>();
        Field[] sourceFields = getDeclaredFields(sourceClass);
        Field[] targetFields = getDeclaredFields(targetClass);
        for (Field sourceField : sourceFields) {
            if (sourceField.getName().equals("serialVersionUID")) {
                continue;
            }
            Field targetField = findCorrespondingField(targetFields, sourceField);
            if (targetField == null) {
                log.warn("No target field found for " + sourceField.getName());
                continue;
            }
            if (Modifier.isFinal(targetField.getModifiers())) {
                log.warn("The target field " + targetField.getName() + " is final, and so cannot be written to");
                continue;
            }
            sourceFieldsToTargetFields.put(sourceField, targetField);
        }
        return Collections.unmodifiableMap(sourceFieldsToTargetFields);
    }

    private Field[] getDeclaredFields(Class<?> clazz) {
        FIELDS.computeIfAbsent(clazz, Class::getDeclaredFields);
        return FIELDS.get(clazz);
    }

    private <S> Object getValue(S source, Field sourceField) throws IllegalArgumentException, IllegalAccessException {
        sourceField.setAccessible(true);
        return sourceField.get(source);
    }

    private <T> void setValue(T target, Field targetField, Object value) throws IllegalArgumentException, IllegalAccessException {
        targetField.setAccessible(true);
        targetField.set(target, value);
    }

    private Field findCorrespondingField(Field[] targetFields, Field sourceField) {
        for (Field targetField : targetFields) {
            if (sourceField.getName().equals(targetField.getName())) {
                if (sourceField.getType().equals(targetField.getType())) {
                    return targetField;
                } else {
                    log.warn("Different types for field " +  sourceField.getName() 
                            + " source " + sourceField.getType() + " and target " + targetField.getType());
                    return null;
                }
            }
        }
        return null;
    }
}

这篇关于通过直接字段访问复制对象属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆