Cómo establecer un campo de Número con un tipo de número diferente utilizando la reflexión de Java

auntyellow:

Nuestro código original es algo así como:

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

Pero el tipo de campo de frijol puede ser diferente del tipo de valor, por ejemplo, nuestro grano es un Integersino valor recuperado de la db puede ser Byteo Short.

¿Cómo puedo escribir un código simple de convertir los tipos en caja sin excepción reflexión?

Aquí está mi solución, pero no es tan simple como mi expectativa:

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 (...) {
   ...
}
Holger:

No hay manera de manejar estos conversión genéricamente a menos que añadir aún más la reflexión.

Lo mejor que puede hacer, es hacer que estas expresiones se requieren más fácil de mantener. Una cosa a tener en cuenta, es que con sus expresiones como, por ejemplo Short.valueOf(((Number) value).shortValue()), la valueOfinvocación es innecesaria. Eso es lo que hace de auto-boxing de todos modos.

A continuación, se puede hacer con las últimas versiones de 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;
}

Para hacer frente a todos los tipos de campos numéricos, ya sea en caja o primitivas. Esto puede ser el uso como

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

para usar una de las conversiones numéricas si procede o simplemente utilizar el setmétodo sin transformaciones de otra manera. Esto último también emitir la excepción apropiado para tipos no coincidentes.

Para versiones anteriores de Java usted podría considerar algo así como

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

Esto sólo se ocupa de los tipos numéricos y devolver el estado de éxito. La persona que llama puede usarlo como

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

Supongo que te gusta

Origin http://10.200.1.11:23101/article/api/json?id=478486&siteId=1
Recomendado
Clasificación