How can you map and pass unknown types in Java when using reflection?

I am using reflection to map getters from one class to setters in another, i.e. I have the form classes used by stuts1 to display mostly text (strings), and I have pure Java objects used by the trailing end that holds values ​​in their specific type. I currently have a mapping between getters and setters, which was easy, but I'm having trouble with mixed types. I am using the parameter type from the setter to find out what was expected and so I need to determine the type of the object from the receiver and specify it as the expected type.

eg.

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

      

Below is my code

/**
     * 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

      

Thanks in advance, Alexey Blue.

+3


source to share


3 answers


In the end, I implemented the best solution:

    /**
     * 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 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

      

This is cleaner than my original solution, but essentially when mapping, the filter is built from methods for the mapping that just loops and then displays. The parse method just checks the types and uses the valueOf method for Object.toString (), which works for standard Java types.



Greetings,

Alexey Blue.

0


source


I'm not a hero in thought, but I'm guessing int

it's a primitive data type and yours attValue

is a type Object

.



Can you try to change the age type to Integer

so that it can be distinguished beforeObject

+1


source


Yes, @Molske is the place. The problem is promoting the return value int

as Integer

and then this comparison:

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

      

I just quickly checked your code and I get the exception:

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

      

Instead fromType

, when compared to methodFrom.getReturnType()

, then the code should work:

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

      

Here's the test I worked with:

@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); }
}

      

0


source







All Articles