NumberUtils在将BigDecimal转换为Long时抛出异常,但在转换为Integer时成功

时间:2016-03-06 03:58:50

标签: java spring

Bool

第一行返回-5但第二行抛出异常System.out.println(org.springframework.util.NumberUtils.convertNumberToTargetClass(new BigDecimal("18446744073709551611"), Integer.class)); System.out.println(org.springframework.util.NumberUtils.convertNumberToTargetClass(new BigDecimal("18446744073709551611"), Long.class));

这是方法java.lang.IllegalArgumentException: Could not convert number [18446744073709551611] of type [java.math.BigDecimal] to target class [java.lang.Long]: overflow的预期结果吗?

2 个答案:

答案 0 :(得分:2)

这是Spring的一个错误。

检查转换为long的边界是否正确。它首先将BigDecimal转换为BigInteger,然后检查它是否在Long的范围内。

88      else if (targetClass.equals(Long.class)) {
89          BigInteger bigInt = null;
90          if (number instanceof BigInteger) {
91              bigInt = (BigInteger) number;
92          }
93          else if (number instanceof BigDecimal) {
94              bigInt = ((BigDecimal) number).toBigInteger();
95          }
96          // Effectively analogous to JDK 8's BigInteger.longValueExact()
97          if (bigInt != null && (bigInt.compareTo(LONG_MIN) < 0 || bigInt.compareTo(LONG_MAX) > 0)) {
98              raiseOverflowException(number, targetClass);
99          }
100         return (T) new Long(number.longValue());
101     }

然而,转换为Integer(和其他原始数字类型)已被破坏。 如果BigDecimal和然后检查此长值是否在类型的范围内,它首先需要longValue。但是,如果它不在长值的范围内,则长值是截断的数字,这是无意义的并且可能看起来在较小类型的范围内,但实际上并不是因为最高位是在初始转换为long时被截断。

在您的示例中,18446744073709551611截断为Long得到-5,这完全在整数范围内 - 但原始数字显然不在整数范围内。

81      else if (targetClass.equals(Integer.class)) {
82          long value = number.longValue();
83          if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
84              raiseOverflowException(number, targetClass);
85          }
86          return (T) new Integer(number.intValue());
87      }

为了正确实现这一点,Spring需要将转换更改为Integer(以及Short和Byte),以便像转换为Long一样工作:首先转换为BigInteger,然后检查范围,然后转换为正确type(整数/字节/短)。

答案 1 :(得分:0)

  

这是方法的预期结果

是和否。

是的,这是指定java.lang.Long并获得Long溢出的预期结果。

不,通常,因为根据方法签名convertNumberToTargetClass(Number number, Class targetClass),如果targetClassjava.lang.Number的子类,那么您不会得到此异常。在你的情况下,java.lang.Long是子类,所以它不会出现,但是你因为溢出而得到了。

来自org.springframework.util.NumberUtils.convertNumberToTargetClass

的代码段
public static <T extends Number> T convertNumberToTargetClass(Number number, Class<T> targetClass) throws IllegalArgumentException {
......
else if (targetClass.equals(Long.class)) {
            BigInteger bigInt = null;
            if (number instanceof BigInteger) {
                bigInt = (BigInteger) number;
            }
            else if (number instanceof BigDecimal) {
                bigInt = ((BigDecimal) number).toBigInteger();
            }
            // Effectively analogous to JDK 8's BigInteger.longValueExact()
            if (bigInt != null && (bigInt.compareTo(LONG_MIN) < 0 || bigInt.compareTo(LONG_MAX) > 0)) {
                raiseOverflowException(number, targetClass);
            }
            return (T) new Long(number.longValue());
        }
      .......
      }

以下是raiseOverflowException的代码段,从您获得例外的地方开始:

    private static void  [More ...] raiseOverflowException(Number number, Class<?> targetClass) {
throw new IllegalArgumentException("Could not convert number [" + number + "] of type [" +
                number.getClass().getName() + "] to target class [" + targetClass.getName() + "]: overflow");
    }

查看org.springframework.util.NumberUtils.convertNumberToTargetClass here

的实施情况