java - 如何使用 Java Reflection 设置不同数字类型的数字字段

标签 java reflection numbers javabeans

我们的原始代码是这样的:

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

但是bean字段的类型可能与值的类型不同,例如我们的bean是一个Integer,但从数据库检索的值可能是ByteShort

如何编写一些简单的代码来转换装箱类型而不会出现反射异常?

这是我的解决方案,但并不像我期望的那么简单:

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

最佳答案

除非添加更多反射,否则无法一般处理这些转换。

您能做的最好的事情就是使这些所需的表达式更易于维护。需要考虑的一件事是你的表达方式,例如: 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;
}

处理所有数字字段类型,无论是原始字段还是装箱字段。这可以像这样使用

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

使用其中一种数字转换(如果适用),或者仅使用 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;
}

这只处理数字类型并返回成功状态。调用者可以像这样使用它

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

关于java - 如何使用 Java Reflection 设置不同数字类型的数字字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60257300/

相关文章:

java - 如何更改H2数据库的密码加密?

java - 如何以编程方式更改 S3 上的文件名以删除文件扩展名?

java - 为什么带有可变参数的 Java 方法被识别为 transient ?

ios - 计算中的小数点为 .或者 ,

java - BNF 语法模仿 Java 方法声明

java - 关于通过java实现的反射的工厂设计模式

.net - ParameterInfo.DefaultValue 和 ParameterInfo.RawDefaultValue 之间的区别

java - 将JSON反序列化为通用映射,强制将所有JSON float 转换为Decimal或String,在Jackson中

c++ - 为什么数字不会保持随机?

java - 为什么 HashMap 为这个程序产生输出 0?