Java 如何使用反射提取(减少)代码以获取字段(整数、长、浮点、双精度)并进行一些计算?

Java 如何使用反射提取(减少)代码以获取字段(整数、长、浮点、双精度)并进行一些计算?,java,reflection,Java,Reflection,我写了一个计算QoQ的方法。 我的想法是迭代所有字段,计算它是长字段、整数字段、浮点字段还是双精度字段,并将字段名和结果设置为映射。 写这段代码很容易,但我发现它太难看了: publicstaticmap-calculateQoq(现在最后一个T,之前最后一个T){ 最终字段[]declaredFields=now.getClass().getDeclaredFields(); if(数组直到isEmpty(申报字段)){ return Collections.emptyMap(); } 最终映

我写了一个计算QoQ的方法。
我的想法是迭代所有字段,计算它是长字段、整数字段、浮点字段还是双精度字段,并将字段名和结果设置为映射。
写这段代码很容易,但我发现它太难看了:

publicstaticmap-calculateQoq(现在最后一个T,之前最后一个T){
最终字段[]declaredFields=now.getClass().getDeclaredFields();
if(数组直到isEmpty(申报字段)){
return Collections.emptyMap();
}
最终映射=新HashMap(declaredFields.length,1);
for(最后一个字段f:now.getClass().getDeclaredFields()){
试一试{
f、 setAccessible(true);
最终对象a=f.get(之前);
if(整数的实例){
最后一个整数beforeNum=(整数)a;
如果(beforeNum==null | | beforeNum==0){
put(f.getName(),零);
继续;
}
最终整数nowNum=(整数)f.get(现在);
if(nowNum==null){
put(f.getName(),零);
继续;
}
put(f.getName(),formatTwoFraction((nowNum-beforeNum)*1.0/beforeNum));
}else if(长的实例){
最后一个Long beforeNum=(Long)a;
如果(beforeNum==null | | beforeNum==0){
put(f.getName(),零);
继续;
}
final Long nowNum=(Long)f.get(now);
if(nowNum==null){
put(f.getName(),零);
继续;
}
put(f.getName(),formatTwoFraction((nowNum-beforeNum)*1.0/beforeNum));
}else if(双精度实例){
最后一个Double beforeNum=(Double)a;
如果(beforeNum==null | | beforeNum==0){
put(f.getName(),零);
继续;
}
最后一个Double nowNum=(Double)f.get(now);
if(nowNum==null){
put(f.getName(),零);
继续;
}
put(f.getName(),formatTwoFraction((nowNum-beforeNum)/beforeNum));
}else if(浮点实例){
最终浮点数beforeNum=(Float)a;
如果(beforeNum==null | | beforeNum==0){
put(f.getName(),零);
继续;
}
final Float nowNum=(Float)f.get(现在);
if(nowNum==null){
put(f.getName(),零);
继续;
}
put(f.getName(),formatTwoFraction((nowNum-beforeNum)/beforeNum));
}
}捕获(最终异常e){
LOG.error(“calculateQoq-get字段失败-”+f.getName(),e);
}
}
返回图;
}
我只是重复了四次几乎相同的逻辑,我尝试使用类似于
voiddoxxx(T before,T now)

但是
数字
无法计算。
Integer、Long和其他没有一些公共接口,如
numberquals
的默认实现等于
执行类型检查)或
可分割

java中也没有宏…
我试了几次,但还没有解决办法


所以我想知道是否有任何方法可以进行抽象并减少这种逻辑

我建议将问题隔离到一个单独的转换方法中,该方法将
Number
作为参数,并返回一个可以统一处理的原语类型。例如:

private static int toInt(Number number) {
    // domain-specific conversion logic
}
然后,可以简化代码,通过使用单个案例避免打开确切的类型:

if(a instanceof Number) {
    int beforeNum = toInt((Number)a);
    if(beforeNum == 0) {
        map.put(f.getName(), ZERO);
        continue;
    }
// and so on
问题的关键在于,如何详细地完成转换将是特定于域的(即,将取决于数字的解释方式)。假设这些数字代表货币,那么乘以100(或使用任何分数单位)并使用整数运算可能更安全。在任何情况下,此代码都可以利用
Number
上的
intValue()
(或其他类似)方法再次避免切换。举个例子:

private static int toInt(Number number) {
    if( number == null ) {
        return 0;
    }
    return ((int) (number.doubleValue() * 100.0)); // Example only
}

下面是一个解决方案,它使用映射来查找类的处理程序
Long
Integer
Double
Float
都由相同的
NumberPercentDifferenceCalculator
类处理
BigInteger
BigDecimal
有自己的处理程序

public class QoQCalculator {
    private static final Map<Class<?>, AbstractPercentDifferenceCalculator> HANDLERS;

    static {
        HANDLERS = new HashMap<>();
        NumberPercentDifferenceCalculator npdc = new NumberPercentDifferenceCalculator();
        HANDLERS.put(Integer.class, npdc);
        HANDLERS.put(Long.class, npdc);
        HANDLERS.put(Float.class, npdc);
        HANDLERS.put(Double.class, npdc);

        BigDecimalPercentDifferenceCalculator bdpc = new BigDecimalPercentDifferenceCalculator();
        HANDLERS.put(BigDecimal.class, bdpc);
        HANDLERS.put(BigInteger.class, new BigIntegerPercentDifferenceCalculator(bdpc));
    }

    public static <T> Map<String, String> calculateQoq(final T now, final T before) {
        final Field[] declaredFields = now.getClass().getDeclaredFields();
        if (declaredFields.length == 0) {
            return Collections.emptyMap();
        }
        final Map<String, String> map = new HashMap<>(declaredFields.length, 1);
        for (final Field f : now.getClass().getDeclaredFields()) {
            try {
                f.setAccessible(true);
                final Object a = f.get(before);
                AbstractPercentDifferenceCalculator calculator = HANDLERS.get(a.getClass());
                if (calculator != null) {
                    map.put(f.getName(), calculator.getPercentDifference(f.get(now), a));
                } else {
                    System.out.println("No handler for " + a.getClass());
                }
            } catch (final Exception e) {
                e.printStackTrace(System.out);
            }
        }
        return map;
    }

}
公共类QoQCalculator{

私有静态最终映射可能使用
Number.getDouble()
因为这是最宽的值。可能仍然需要对
biginger
BigDecimal
进行特殊处理。这很聪明,
Number
不能直接使用,但子类实现了它的抽象方法。我几乎忘记了使用多态性,thx很多。很好的抽象,使用
getClass()
方法,可以以更一般的方式使用。thx很多。:)
abstract class AbstractPercentDifferenceCalculator<T> {
    private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##%");
    private static final String ZERO = DECIMAL_FORMAT.format(0);

    public final String getPercentDifference(Object now, Object before) {
        if (returnZeroForNow(cast(now)) || returnZeroForBefore(cast(before))) {
            return ZERO;
        }

        return DECIMAL_FORMAT.format(calculatePercentDifference(cast(now), cast(before)));
    }

    protected abstract double calculatePercentDifference(T now, T before);

    protected abstract T cast(Object o);

    protected boolean returnZeroForNow(T now) {
        return now == null;
    }

    protected boolean returnZeroForBefore(T before) {
        return before == null;
    }

}
class NumberPercentDifferenceCalculator extends AbstractPercentDifferenceCalculator<Number> {

    @Override
    protected double calculatePercentDifference(Number now, Number before) {
        return (now.doubleValue() - before.doubleValue()) / before.doubleValue();
    }

    @Override
    protected Number cast(Object o) {
        return (Number) o;
    }

    @Override
    protected boolean returnZeroForBefore(Number before) {
        return super.returnZeroForBefore(before) || before.doubleValue() == 0D;
    }

}


class BigDecimalPercentDifferenceCalculator extends AbstractPercentDifferenceCalculator<BigDecimal> {

    protected double calculatePercentDifference(BigDecimal now, BigDecimal before) {
        return now.subtract(before).divide(before, BigDecimal.ROUND_CEILING).doubleValue();
    }

    @Override
    protected BigDecimal cast(Object o) {
        return (BigDecimal) o;
    }

    @Override
    protected boolean returnZeroForBefore(BigDecimal before) {
        return super.returnZeroForBefore(before) || before.compareTo(BigDecimal.ZERO) == 0;
    }

}


class BigIntegerPercentDifferenceCalculator extends AbstractPercentDifferenceCalculator<BigInteger> {
    public final BigDecimalPercentDifferenceCalculator delegate;

    public BigIntegerPercentDifferenceCalculator(BigDecimalPercentDifferenceCalculator delegate) {
        this.delegate = delegate;
    }

    @Override
    protected BigInteger cast(Object o) {
        return (BigInteger) o;
    }

    @Override
    protected double calculatePercentDifference(BigInteger now, BigInteger before) {
        return delegate.calculatePercentDifference(new BigDecimal(now), new BigDecimal(before));
    }

    @Override
    protected boolean returnZeroForBefore(BigInteger before) {
        return delegate.returnZeroForBefore(new BigDecimal(before));
    }

}
public class QoQRunner {

    public static void main(String[] args) {
        Holder before = new Holder(1, 2.0, 3L, BigDecimal.valueOf(4.4), BigInteger.valueOf(5));
        Holder now = new Holder(10, 12.0, 13L, BigDecimal.valueOf(2.2), BigInteger.valueOf(15));
        Map<String, String> diff = QoQCalculator.calculateQoq(now, before);
        System.out.println(diff);
    }

    @Data  // from lombok
    @AllArgsConstructor // from lombok
    public static class Holder {
        private final Integer i;
        private final Double d;
        private final Long l;
        private final BigDecimal bd;
        private final BigInteger bi;
    }

}