Как вы можете отобразить и привести неизвестные типы в Java при использовании отражения? - PullRequest
3 голосов
/ 16 марта 2012

Я использую отражение для сопоставления методов получения из одного класса с установщиками в другом, то есть у меня есть классы форм, используемые stuts1 для отображения в основном текста (Strings), и у меня есть чистые объекты Java, используемые серверной частью, которая содержит значения в конкретный тип. В настоящее время у меня работает отображение между геттерами и сеттерами, что было легко, но у меня проблемы со смешанными типами. Я использую тип параметра из установщика, чтобы увидеть, что ожидается, и поэтому мне нужно определить тип объекта из геттера и привести его как к ожидаемому типу.

* 1003 Е.Г. *

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 ]

1 голос
/ 16 марта 2012

Я не герой в размышлениях, но я предполагаю, что int является примитивным типом данных, а ваш attValue имеет тип Object.

Можете ли вы попробовать изменить типв возрасте до Integer, чтобы его можно было привести к Object

0 голосов
/ 16 марта 2012

В итоге я реализовал лучшее решение:

    /**
     * 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 просто проверяет типы и использует метод valueOf в Object.toString (), который работает для стандартных типов Java.

Cheers,

Alexei Blue.

0 голосов
/ 16 марта 2012

Да, @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); }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...