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