在使用反射时,如何在Java中映射和转换未知类型?

时间:2012-03-16 10:45:33

标签: java reflection types casting

我使用反射将getter从一个类映射到另一个类中的setter,即我有stuts1使用的表单类,主要用于显示文本(字符串),我有后端使用的纯Java对象,它保存值在他们的特定类型。我目前在getter和setter之间进行映射工作很容易,但是我遇到了混合类型的问题。我使用setter中的参数类型来查看预期的内容,因此我需要从getter中确定对象的类型,并将其转换为预期的类型。

E.g。

HomePageForm  ->   HomePageData 
Name="Alexei" ->   String name; (Maps correctly) 
Age="22"      ->   int age;     (Needs casting from string to int and visa-vera in reverse)

以下是我目前的代码

/**
     * Map the two given objects by reflecting the methods from the mapTo object and finding its setter methods.  Then 
     * find the corresponding getter method in  the mapFrom class and invoke them to obtain each attribute value.  
     * Finally invoke the setter method for the mapTo class to set the attribute value.  
     * 
     * @param mapFrom The object to map attribute values from
     * @param mapTo   The object to map attribute values to
     */
    private void map(Object mapFrom, Object mapTo) {
        Method [] methods = mapTo.getClass().getMethods();
        for (Method methodTo : methods) {
            if (isSetter(methodTo)) {
                try {
                    //The attribute to map to setter from getter
                    String attName = methodTo.getName().substring(3);

                    //Find the corresponding getter method to retrieve the attribute value from
                    Method methodFrom = mapFrom.getClass().getMethod("get" + attName, new Class<?>[0]);

                    //If the methodFrom is a valid getter, set the attValue
                    if (isGetter(methodFrom)) {
                        //Invoke the getter to get the attribute to set
                        Object attValue = methodFrom.invoke(mapFrom, new Object[0]);

                        Class<?> fromType = attValue.getClass();

                        //Need to cast/parse type here
                        if (fromType != methodTo.getParameterTypes()[0]){
                            //!!Do something to case/parse type!!
                        } //if

                        //Invoke the setter to set the attribute value
                        methodTo.invoke(mapTo, attValue);
                    } //if
                } catch (Exception e) {
                    Logger.getLogger(Constants.APP_LOGGER).fine("Exception in DataFormMappingService.map: "
                                                              + "IllegalArgumentException" + e.getMessage());
                    continue;
                }
            } //if
        } //for
    } //map

提前致谢, 阿列克谢蓝。

3 个答案:

答案 0 :(得分:1)

我不是反射中的英雄,但我的猜测是int是原始数据类型,attValue类型为Object

您可以尝试将年龄类型更改为Integer,以便将其转换为Object

答案 1 :(得分:0)

是的,@ Molske是现货。问题是将int返回值提升为Integer,然后进行比较:

if (fromType != methodTo.getParameterTypes()[0])

我刚刚对你的代码进行了快速测试,我得到了例外:

java.lang.AssertionError: expected:<class java.lang.Integer> but was:<int>

而不是fromType,如果您与methodFrom.getReturnType()进行比较,则代码应该有效:

if (methodFrom.getReturnType() != methodTo.getParameterTypes()[0])

这是我正在进行的测试:

@Test
public void testException() throws Exception {
    Foo foo = new Foo();
    Bar bar = new Bar();
    for (Method methodTo : Bar.class.getMethods()) {
        if (methodTo.getName().startsWith("set")) {
            String attName = methodTo.getName().substring(3);
            Method methodFrom = Foo.class.getMethod("get" + attName);
            if (methodFrom != null && methodFrom.getName().startsWith("get")) {
                Object attValue = methodFrom.invoke(foo);
                // this ignores the auto-boxing issues
                assertEquals(methodFrom.getReturnType(),
                    methodTo.getParameterTypes()[0]);
                methodTo.invoke(bar, attValue);
            }
        }
    }
}
private static class Foo {
    public int getFoo() { return 1; }
}
private static class Bar {
    public void setFoo(int i) { System.out.println("i = " + i); }
}

答案 2 :(得分:0)

我最终实施了一个更好的解决方案:

    /**
     * Map to given objects taking into account the inclusion and exclusion sets.
     * 
     * @param mapFrom The object to map attribute values from
     * @param mapTo   The object to map attribute values to
     */
    private void map(Object mapFrom, Object mapTo)
    {
        setMapFilter(mapFrom, mapTo);

        for (Method sMethod : filterMap.keySet())
        {            
            try
            {
               //Find the corresponding getter method to retrieve the attribute value from
                Method gMethod = filterMap.get(sMethod);

                //Invoke the getter to get the attribute to set
                Object attValue = gMethod.invoke(mapFrom, new Object[0]);

                //Get the mapping types and check their compatibility, if incompatible convert attValue type to toType
                Class<?> fromType = gMethod.getReturnType();
                Class<?> toType   = sMethod.getParameterTypes()[0];

                if(!toType.isAssignableFrom(fromType) && Objects.isNotNull(attValue))
                {
                    attValue = parseType(attValue, toType);
                } //if

                //Invoke the setter to set the attribute value
                sMethod.invoke(mapTo, attValue);
            }
            catch (IllegalArgumentException e)
            {
                Logger.getLogger(Constants.APP_LOGGER).fine("Exception in DataFormMappingService.map: "
                                                          + "IllegalArgumentException " + e.getMessage());
                continue;
            }
            catch (IllegalAccessException e)
            {
                Logger.getLogger(Constants.APP_LOGGER).fine("Exception in DataFormMappingService.map: "
                                                          + "IllegalAccessException " + e.getMessage());
                continue;
            }
            catch (InvocationTargetException e)
            {
                Logger.getLogger(Constants.APP_LOGGER).fine("Exception in DataFormMappingService.map: "
                                                          + "InvocationTargetException " + e.getMessage());
                continue;
            }
        } //for each
    } //map

    /**
     * Refactored method to parse an object, attValue, from it's unknown type to the type expected.
     * 
     * @param attValue The attribute value to parse
     * @param type     The type expected/to convert to
     * @return         The attribute value in the expected type, null otherwise
     */
    private Object parseType(Object attValue, Class<?> type)
    {
        Object newAttValue = null;

        if (isLiteral(type))
        {
            newAttValue = attValue.toString();
        }
        else if (isByte(type))
        {
            newAttValue = Byte.valueOf(attValue.toString());
        }
        else if (isInt(type))
        {
            newAttValue = Integer.valueOf(attValue.toString());
        }
        else if (isShort(type))
        {
            newAttValue = Short.valueOf(attValue.toString());
        }
        else if (isLong(type))
        {
            newAttValue = Long.valueOf(attValue.toString());
        }
        else if (isFloat(type))
        {
            newAttValue = Float.valueOf(attValue.toString());
        }
        else if (isDouble(type))
        {
            newAttValue = Double.valueOf(attValue.toString());
        }
        else if (isBoolean(type))
        {
            newAttValue = Boolean.valueOf(attValue.toString());
        } //if-else if*

        return newAttValue;
    } //parseType

它比我原来的解决方案更干净,但基本上在映射时,过滤器是由映射方法构建的,这些方法只需循环遍历然后映射。 parse方法只检查类型并在Object.toString()上使用valueOf方法,该方法适用于标准Java类型。

干杯,

Alexei Blue。