Как установить числовое поле с другим числовым типом, используя Java Отражение - PullRequest
2 голосов
/ 17 февраля 2020

Наш оригинальный код выглядит примерно так:

Object bean = ...
Field field = ...
Object value = ... // Retrieved from Database
field.set(bean, value);

Но тип поля bean-компонента может отличаться от типа значения, например, наш bean-компонент - Integer, но значение, полученное из db, может быть Byte или Short.

Как мне написать простой код для преобразования коробочных типов без исключения отражения?

Вот мое решение, но не такое простое, как ожидалось:

if (field.getType().equals(Integer.class)) {
   field.set(bean, Integer.valueOf(((Number) value).intValue()));
} else if (field.getType().equals(Short.class)) {
   field.set(bean, Short.valueOf(((Number) value).shortValue()));
} else if (...) {
   ...
}

1 Ответ

3 голосов
/ 17 февраля 2020

Нет никакого способа обработать это преобразование, если вы не добавите еще больше Reflection.

Лучшее, что вы можете сделать, - это сделать эти обязательные выражения более понятными. Следует учитывать, что с такими выражениями, как, например, Short.valueOf(((Number) value).shortValue()), вызов valueOf не требуется. Вот что делает автобокс в любом случае.

Затем вы можете сделать с последними Java версиями:

static final Map<Class<?>,Setter> SETTERS =Map.copyOf(Map.<Class<?>,NumericSetter>ofEntries(
    Map.entry(byte.class, (field,bean,n) -> field.setByte(bean, n.byteValue())),
    Map.entry(short.class, (field,bean,n) -> field.setShort(bean, n.shortValue())),
    Map.entry(int.class, (field,bean,n) -> field.setInt(bean, n.intValue())),
    Map.entry(long.class, (field,bean,n) -> field.setLong(bean, n.longValue())),
    Map.entry(float.class, (field,bean,n) -> field.setFloat(bean, n.floatValue())),
    Map.entry(double.class, (field,bean,n) -> field.setDouble(bean, n.doubleValue())),
    Map.entry(Byte.class, (field,bean,n) -> field.set(bean, n.byteValue())),
    Map.entry(Short.class, (field,bean,n) -> field.set(bean, n.shortValue())),
    Map.entry(Integer.class, (field,bean,n) -> field.set(bean, n.intValue())),
    Map.entry(Long.class, (field,bean,n) -> field.set(bean, n.longValue())),
    Map.entry(Float.class, (field,bean,n) -> field.set(bean, n.floatValue())),
    Map.entry(Double.class, (field,bean,n) -> field.set(bean, n.doubleValue()))
));
interface Setter {
    void set(Field field, Object bean, Object value) throws IllegalAccessException;
    Setter FALLBACK = Field::set;
}
interface NumericSetter extends Setter {
    @Override default void set(Field field, Object bean, Object value)
    throws IllegalAccessException {
        setNumeric(field, bean, (Number)value);
    }
    void setNumeric(Field f, Object bean, Number n) throws IllegalAccessException;
}

Для обработки всех числовых c типов полей, примитивных или коробочных. Это можно использовать как

SETTERS.getOrDefault(field.getType(), Setter.FALLBACK).set(field, bean, value);

, чтобы использовать одно из преобразований цифр c, если это применимо, или просто использовать метод set без преобразований в противном случае. Последний также выдает соответствующее исключение для несовпадающих типов.

Для более старых Java версий вы можете рассмотреть что-то вроде

static boolean setNumeric(Field field, Object bean, Object value)
throws IllegalAccessException {
    if(!(value instanceof Number)) return false;
    Number n = (Number)value;
    Class<?> type = field.getType();
    if(type.isPrimitive()) {
        if(type == boolean.class || type == char.class) return false;
        switch(type.getName().charAt(0)) {
            case 'b': field.setByte(bean, n.byteValue()); break;
            case 's': field.setShort(bean, n.shortValue()); break;
            case 'i': field.setInt(bean, n.intValue()); break;
            case 'l': field.setLong(bean, n.longValue()); break;
            case 'f': field.setFloat(bean, n.floatValue()); break;
            case 'd': field.setDouble(bean, n.doubleValue()); break;
            default: throw new AssertionError(type);
        }
    }
    else {
        if(!Number.class.isAssignableFrom(type)
        || type.getPackage() != Object.class.getPackage())
            return false;
        switch(type.getSimpleName().charAt(0)) {
            case 'B': field.set(bean, n.byteValue()); break;
            case 'S': field.set(bean, n.shortValue()); break;
            case 'I': field.set(bean, n.intValue()); break;
            case 'L': field.set(bean, n.longValue()); break;
            case 'F': field.set(bean, n.floatValue()); break;
            case 'D': field.set(bean, n.doubleValue()); break;
            default: throw new AssertionError(type);
        }
    }
    return true;
}

. Это обрабатывает только числовые типы c и возвращает статус успеха. Абонент может использовать его как

if(!setNumeric(field, bean, value)) {
    field.set(bean, value); // non numeric or throw appropriate exception
}
...