将一个类的字段复制到另一个相同字段的类中。

15

我有这个问题,但是由于我不知道要使用的确切术语,所以很难解释。希望有人能理解。我会尽力描述得更好。我感觉这与解析有很大关联。

假设有两个类。在这两个类中,我都有一些变量,比如字符串(仅为简单起见,变量类型可以是任何类型),它们具有相似的名称

Eg:
    class ClassA{
        String x,y,z;
    }

    class ClassB{
        String x,y,z;
    }

现在,我需要的是将一个类的变量值复制到其他类的相应变量中。

Eg:
    ClassA aa=new ClassA();
    ClassB bb=new ClassB();
    //set bb's variables
    aa.x=bb.x;
    aa.y=bb.y;
    aa.z=bb.z;

就像那样。

但请注意,我需要的不是上述方法。我希望有一种简单的方法,通过传递名称来识别相关变量,然后相应地进行值赋值。

我想象中的方法是这样的,

void assign(String val){        
    // aa.<val>=val
}
例如,如果您将bb.x传递给assign(...)方法,则它将执行aa.x=bb.x赋值操作。希望这足够清楚了。一定有更好的方法来解释这个问题。如果有人知道,请编辑该帖子(+标题)以使其更清晰(但要保留我的想法)。请告诉我是否有一种实现这个的方法。谢谢!

@Shark,你说的“为什么不与我们分享一个常见用例”是什么意思?(我看到你把你的答案改成了评论:p) - Anubis
我的意思是“更好地告诉我们这可能在哪里以及如何使用,因为您可能从一开始就有错误的解决问题的想法。” ClassA、ClassBB太模糊了,实际上并没有描述任何内容;共享真实的类名可能会让人们了解它们应该做什么。 - Shark
1
@Shark,我正在寻找像Jean-Rémy Revy和Sean Patrick Floyd在下面建议的解决方案。不要被类名所迷惑。这是我能做到的最好的解释。如果我试图提供更多细节,那将会很复杂。 - Anubis
@Shark 简单来说,我需要一个方法,可以识别传递的变量名称并处理另一个独立对象中的同名变量(两个变量将是相同类型)。 - Anubis
2
谢谢@Shark,如果您能解释一下“反射”,那将不胜感激。我还没有遇到过这个..(最好把它放在答案中,因为这不像是扩展讨论:D) - Anubis
显示剩余6条评论
4个回答

16

多瑟没问题,可以看看Jean-Remy的回答。

此外,如果变量按照JavaBeans标准定义了getter和setter方法,有很多技术可以帮助您,例如Apache Commons / BeanUtils

示例代码(未经过测试):

final Map<String, Object> aProps = BeanUtils.describe(a);
final Map<String, Object> bProps = BeanUtils.describe(b);
aProps.keySet().retainAll(bProps.keySet());
for (Entry<String, Object> entry : aProps.entrySet()) {
    BeanUtils.setProperty(b,entry.getKey(), entry.getValue());
}

更新:

如果你没有getter和setter方法,这是一个快速的技巧,可以将一个类的字段值复制到另一个类中,只要这些字段有相同的名称和类型。我没有测试过它,但应该可以作为一个起点:

public final class Copier {

    public static void copy(final Object from, final Object to) {
        Map<String, Field> fromFields = analyze(from);
        Map<String, Field> toFields = analyze(to);
        fromFields.keySet().retainAll(toFields.keySet());
        for (Entry<String, Field> fromFieldEntry : fromFields.entrySet()) {
            final String name = fromFieldEntry.getKey();
            final Field sourceField = fromFieldEntry.getValue();
            final Field targetField = toFields.get(name);
            if (targetField.getType().isAssignableFrom(sourceField.getType())) {
                sourceField.setAccessible(true);
                if (Modifier.isFinal(targetField.getModifiers())) continue;
                targetField.setAccessible(true);
                try {
                    targetField.set(to, sourceField.get(from));
                } catch (IllegalAccessException e) {
                    throw new IllegalStateException("Can't access field!");
                }
            }
        }
    }

    private static Map<String, Field> analyze(Object object) {
        if (object == null) throw new NullPointerException();

        Map<String, Field> map = new TreeMap<String, Field>();

        Class<?> current = object.getClass();
        while (current != Object.class) {
            for (Field field : current.getDeclaredFields()) {
                if (!Modifier.isStatic(field.getModifiers())) {
                    if (!map.containsKey(field.getName())) {
                        map.put(field.getName(), field);
                    }
                }
            }
            current = current.getSuperclass();
        }
        return map;
    }
}

调用语法:

Copier.copy(sourceObject, targetObject);

感谢@SeanPatrickFloyd。是的,如果我有“getter”和“setter”,这将非常方便。好吧,我会考虑的。但现在我想尝试一下那个“dozer”的东西。 - Anubis
1
@Anubis 我已经为仅字段添加了一个解决方案。 - Sean Patrick Floyd
@Shark 是的,但请注意:这只是10分钟的代码。不要在生产中使用。 - Sean Patrick Floyd
1
我更多地是想证明我下面所建议的概念。但是,如果它有效的话... :) - Shark
2
非常好! 在 "while (current != Object.class)" 循环的末尾缺少 "current = current.getSuperclass();"。没有这个,它会陷入无限循环中。 - jett
显示剩余4条评论

8

你听说过Dozer吗?: http://dozer.sourceforge.net/

Dozer

Dozer是一个Java Bean到Java Bean的映射器,可以递归地从一个对象复制数据到另一个对象。通常,这些Java Beans将是不同的复杂类型。

Dozer支持简单属性映射、复杂类型映射、双向映射、隐式显式映射以及递归映射。这包括需要在元素级别进行映射的集合属性映射。

Dozer允许您映射Java Beans:

  • 使用它们的名称(隐式映射),即将ClassA.x映射到ClassB.x
  • 提供了使用(相当简单的)XML或注释配置映射具有不同名称的不同结构的能力(显式映射)。

这是该库网站上的一个XML示例:

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net
          http://dozer.sourceforge.net/schema/beanmapping.xsd">
          
  <mapping> 
    <class-a>org.dozer.vo.TestObject</class-a>
    <class-b>org.dozer.vo.TestObjectPrime</class-b>   
    <field>
      <a>one</a>
      <b>onePrime</b>
    </field>
  </mapping>  

      <!-- SNIP ... -->

</mappings>

这将映射对象org.dozer.vo.TestObject到TestObjectPrime,将所有相同的变量(就像你的情况一样)和TestObjectFoo.oneFoo变量映射到TestObjectFooPrime.oneFooPrime。
很棒,不是吗?

感谢您的回答。是的,我从未听说过“dozer”。看起来很不错。我会尝试一下并反馈结果。谢谢! - Anubis

5

请看这里。只需使用BeanUtils.copyProperties(newObject, oldObject);即可。


3

新的答案。

我建议研究Dover,因为它似乎很直观。

第二个选项是将类序列化为XML,仅在匹配成员时将其反序列化为目标类。

我在评论中提到的第三个选项是使用反射 - http://java.sun.com/developer/technicalArticles/ALT/Reflection/

这种技术允许使用一种称为内省的漂亮设计模式 - Java内省和反射,从而允许您发现某个类的成员...

现在,话虽如此,人们只需“发现”ClassA的成员,用它们的名称填充ArrayList,然后发现ClassB的成员,用它们的名称填充另一个ArrayList,并复制相交集的值。至少这是我的想法。


3
这应该只是一条评论,不过最好了。 - Sean Patrick Floyd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接