如何使用来自另一个不相关对象的数据快速填充Java对象?

时间:2009-12-22 01:40:34

标签: java

如何使用来自任何其他任意对象的数据填充简单的Java对象?

例如,我最终得到一个Document对象,其子节点可以迭代,我想在另一个对象中设置具有节点值的同名属性。

我主要使用动态语言工作,我认为我很想知道它在perl或javascript中是如何工作的,并且不能让我的动态排水沟长时间看清楚。

我知道我可以做(伪代码)

之类的事情
while (key = nextKey) {
    if (key.name == "fooBar") {
        object.setFooBar(key.value);
    } else if (key.name == "bazQux") {
        object.setBazQux(key.value);
    }
    ...etc...
}

但这感觉并不好,当属性数量或复杂性增加时感觉很糟糕。

在动态语言中,我会做类似的事情:

while (key = nextKey) {
    object.setField(key.name, key.value);
    // or even
    object.[key.name] = key.value;
}

其中setField可以是带代码引用的调度表。我知道默认情况下我没有奢侈的每个对象都是哈希,但我正在寻找一般建议。你会怎么做?

一个开关/案例会好一点,但是java抱怨它不喜欢这些语句中的字符串。枚举会成为解决方案吗? < / blind stabbing for answers>

我已经研究过使用反射来实现某种自动调度表,但这让我感到沮丧,并且必须有更好的方法。

感谢您的任何见解。

10 个答案:

答案 0 :(得分:4)

之前讨论过这个主题:any tool for java object to object mapping?特别是post of Pascal Thivent包含API的 lot 。当然必须有一个适合您的需求。

您还可以根据Reflection APIJavaBeans API(每个PropertyEditor下)决定自制一个。但我认为这需要花费更多的时间,而不是期望让它变得健壮。

答案 1 :(得分:3)

另一次反思投票。您在这里尝试做的是自动将无类型的String数据绑定到类型化的Java变量。这只需要反思。

反思是“被忽视”的原因是因为一般来说,盲目地将无类型的String数据绑定到类型化的Java变量是“被忽视”。在许多情况下,最好(例如)让您的对象明确地仅提取它所需的数据。例如:

this.fooBar = doc.get("fooBar");
this.bazQux = doc.get("bazQux");
// ... etc.

你可能会说,“但是现在如果我在我的文档中添加一个sysBiz条目,我必须记住在我的构造函数中添加一个额外的行。在动态语言中,它将Just Work [tm]!”这是真的,但在Java中你必须添加你的sysBiz成员变量,你的getter和setter,以及sysBiz周围的所有其他逻辑,所以你的构造函数或反序列化部分中的额外行不是这太多麻烦了。此外,它还明确了您关注的文档的哪些部分,因此如果有人在文档中添加zigBlog条目,则不会导致自动绑定出现问题(例如object.setField(key, val) )抛出关于不存在zigBlog字段的运行时错误。

所有这一切,有时候自动将无类型的String数据绑定到类型化的Java变量 是正确的方法,而反射是在该场景中使用的正确工具。确保自动绑定确实是您所拥有的数据结构的最佳选择,但是一旦您确定,就没有必要犹豫使用反射只是因为它“被忽视”或者是稍微更高级的部分语言。这就是它的用途。

答案 2 :(得分:2)

将objectA序列化为XML并将反序列化XML转换为objectB可以解决您的问题吗?

如果是这样,请查看Simple framework。将使您的XML序列化变得轻而易举。 如果没有,那么没关系,跳过我的回答:)

答案 3 :(得分:1)

我认为来自apache的MethodUtils库正是你正在寻找的

答案 4 :(得分:1)

让我解释一下,在Java运行时中执行类似操作的唯一方法是使用反射。这并不是说你必须使用Java语言来实现这一点!我相信Groovy允许你编写与第二代码非常相似的代码,并使用反射编译成字节码。

编辑:也就是说,一般来说 。人们可以想出一些模式来将一个类转换为另一个类。但是通常将一个对象中的字段映射到另一个对象中具有匹配名称的字段,这是一个不同的问题。它可以用Java完成。我以前用Java做过。这很痛苦。让别的东西为你做大部分繁重的工作。

已经有一段时间了,但这是Groovy的尝试:

// Assuming document implements Map.
document.each {

    // At this point, 'it' is a Map.Entry.
    // Make sure the property exists on the target object.
    if (object.properties.keySet().contains(it.key)) {

        // Set the property to the value from the map entry.
        object."${it.key}" = it.value
    }
}

答案 5 :(得分:1)

Java方式类似于:

public interface Trasnformer<F, T>
{
    T transformFrom(F original);
}

然后有类:

public class IntegerStringTransofromer<Integer, String>
{
    public String transformFrom(Integer original)
    {
        // code
    }
}

我相信你可以想出一些更像你真正想做的事情,但这并不是用Java做这件事的自然方式。通常,当您切换到不同类型的语言时,您需要切换处理问题的方式。我确信作为Java程序编写的perl程序将是一件奇怪的事情......就像编写为perl的Java程序一样。

叹息......尽管如此,我还有更好的判断......这里有一个解决方案可以做你所要求的(不完全是,但它是一个开始)......我只要求你不要使用它! / p>

class Copier
{
    public static void copyFromTo(final Object source,
                                  final Object dest)
    {
        final Class   sourceClass;
        final Class   destClass;
        final Field[] sourceFields;

        sourceClass  = source.getClass();
        destClass    = dest.getClass();
        sourceFields = sourceClass.getDeclaredFields();

        for(final Field field : sourceFields)
        {
            copyField(field, source, dest, destClass);
        }
    }

    private static void copyField(final Field field,
                                  final Object source,
                                  final Object dest,
                                  final Class  destClass)
    {
        final String fieldName;
        final String methodName;

        fieldName  = field.getName();
        methodName = "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);

        try
        {
            final Method method;

            method = destClass.getMethod(methodName, field.getType());
            field.setAccessible(true);
            method.invoke(dest, field.get(source));
        }
        catch(final NoSuchMethodException ex)
        {
            // ignore it
            ex.printStackTrace();
        }
        catch(final IllegalAccessException ex)
        {
            // ignore it
            ex.printStackTrace();
        }
        catch(final IllegalArgumentException ex)
        {
            // ignore it
            ex.printStackTrace();
        }
        catch(final InvocationTargetException ex)
        {
            // ignore it
            ex.printStackTrace();
        }
    }
}

以下是如何使用它(再次请不要)。

public class Main
{
    public static void main(String[] args)
    {
        final B b;

        b = new B();
        Copier.copyFromTo(new A(), b);
        System.out.println("b.a = " + b.getA());
        System.out.println("b.b = " + b.getB());
        System.out.println("b.c = " + b.getC());
        System.out.println("b.d = " + b.getD());
        System.out.println("b.e = " + b.getE());
    }
}

class A
{
    private String a = "This";
    private String b = "Is";
    private String c = "A";
    private String d = "Bad";
    private String e = "Idea";
}

class B
{
    private String a;
    private String b;
    private String c;
    private String d;
    private String e;

    public void setA(final String val)
    {
        a = val;
    }

    public void setB(final String val)
    {
        b = val;
    }

    public void setC(final String val)
    {
        c = val;
    }

    public void setD(final String val)
    {
        d = val;
    }

    public void setE(final String val)
    {
        e = val;
    }

    public String getA()
    {
        return (a);
    }

    public String getB()
    {
        return (b);
    }

    public String getC()
    {
        return (c);
    }

    public String getD()
    {
        return (d);
    }

    public String getE()
    {
        return (e);
    }
}

答案 6 :(得分:1)

查看java.lang.reflect中的类。使用反射工具可以构建(类,)方法和字段的名称,然后进行所需的调用。但是,如果调用“setFoo(...)”,请确保具有正确的数据类型。抱歉。欢迎来到Java: - )

答案 7 :(得分:1)

Apache Commons BeanUtils类似于在bean之间移动数据(请注意,基于反射!)

你可以轻松地用反射做一些适合你的问题的事情,然后对它进行单元测试然后 - 如果表现令人满意,那就留下吧。如果没有,您可以考虑使用基于XML模式的代码生成,或使用类似JAXB的东西。

如果你适当地隔离魔法,确保你能很好地处理边缘情况,并且对你的应用程序的性能感到满意,那么对这些事情使用反射确实没有错。

所以......如果像往常一样使用你的大脑,反射并不像大多数时候描绘的那样邪恶。

答案 8 :(得分:1)

我想在apache beans库(http://commons.apache.org/beanutils/),这可能是你最好的选择。 [copyProperties] [1]方法可能就是你要找的。

根据我的经验,这些是您可能遇到的问题:

  1. 他们之间不兼容的对象。该方法将尝试将本机类型(如int)转换为String等,但不会转换复杂类型。课程BeanUtilsBean是您提供自定义转换器的方式;
  2. 我认为你无法控制可以复制的内容(反射方法的问题在于它发生在引擎盖下 - 你可以在类中添加参数A,向同一个类添加相同的参数A,它们不兼容,突然间你的代码将不再起作用了。)
  3. 另一个仍在使用反射的选项是在源代码中使用注释来控制可以复制的内容。您可以在getter方法中添加注释以指示它可以被复制,并且您可以为注释提供一个类,以便在需要时执行类型转换。这同样适用于您的setter方法,如果注释位于其中,则只允许设置它。

    如果你真的想走得太远并想在其构造函数中初始化复制的类,可以在构造函数参数中使用参数注释来执行以下操作:

    public class YourClass {
        public YourClass(
            @DynamicParameter(name = "id") String id,
            @DynamicParameter(name = "name") String name
        )
    }
    

    ...然后使用反射从类中读取构造函数,并使用方法getParameterAnnotations读取注释。仍然,commons bean库将为您提供从本机类型转换为本机类型的方法。

    这完全取决于你想走多远。

    [1]:http://commons.apache.org/beanutils/v1.8.2/apidocs/org/apache/commons/beanutils/BeanUtils.html#copyProperties(java.lang.Object,java.lang.Object)

答案 9 :(得分:1)

Spring BeanUtils也可用于将数据从一个对象复制到其他对象。