Преобразование BigDecimal в Integer

У меня есть метод Hibernate, который возвращает мне BigDecimal. У меня есть другой метод API, которому нужно передать это число, но он принимает параметр Integer как параметр. Я не могу изменять типы возвращаемых данных или переменные типы обоих методов.

Теперь, как преобразовать BigDecimal в Integer и передать его второму методу?

Есть ли выход из этого?

Ответ 1

Вы бы назвали myBigDecimal.intValueExact() (или просто intValue()), и он даже выбросит исключение, если вы потеряете информацию. Это возвращает int, но autoboxing позаботится об этом.

Ответ 2

Можете ли вы гарантировать, что BigDecimal никогда не будет содержать значение больше Integer.MAX_VALUE?

Если да, то здесь ваш код вызывает intValue:

Integer.valueOf(bdValue.intValue())

Ответ 3

Хорошо, вы могли бы назвать BigDecimal.intValue():

Преобразует этот BigDecimal в int. Это преобразование аналогично сужению примитивного преобразования от двойного до короткого, как определено в Спецификации языка Java: любая дробная часть этого BigDecimal будет отброшена, и если полученный BigInteger слишком велик, чтобы вписаться в int, Возвращаются 32 бита. Обратите внимание, что это преобразование может потерять информацию об общей величине и точности этого значения BigDecimal, а также вернуть результат с противоположным знаком.

Затем вы можете либо явно вызвать Integer.valueOf(int), либо позволить автоматически делать бокс для вас, если вы используете достаточно недавнюю версию Java.

Ответ 4

TL; DR

Используйте один из них для универсального преобразования

//Java 7 or below
bigDecimal.setScale(0, RoundingMode.DOWN).intValueExact()
//Java 8    
bigDecimal.toBigInteger().intValueExact()

Рассуждение

Ответ зависит от того, какие требования и как вы отвечаете на этот вопрос.

  • Может ли BigDecimal иметь ненулевую дробную часть?
  • Будет ли BigDecimal потенциально не соответствовать диапазону Integer?
  • Вам нужны ненулевые дробные части, округленные или усеченные?
  • Как вам понравится ненулевая дробная часть?

Если вы ответили "нет" на первые 2 вопроса, вы можете просто использовать BigDecimal.intValueExact(), как предложили другие, и позволить ему взорваться, когда произойдет что-то неожиданное.

Если вы не уверены на 100% в вопросе номер 2, тогда intValue() всегда является неправильным ответом.

Улучшение

Можно использовать следующие предположения, основанные на других ответах.

  • Мы в порядке с потерей точности и усечением значения, потому что intValueExact() и авто-бокс
  • Мы хотим получить исключение, если BigDecimal больше, чем диапазон Integer, потому что все остальное будет сумасшедшим, если у вас нет особой необходимости обертывания, которое случается, когда вы бросаете старшие разряды.

Учитывая эти параметры, intValueExact() выдает исключение, если мы не хотим, чтобы наша дробная часть была ненулевой. С другой стороны, intValue() не генерирует исключения, если это необходимо, если наш BigDecimal слишком большой.

Чтобы получить лучшее из обоих миров, сначала закруглите BigDecimal, а затем преобразуйте. Это также дает вам больший контроль над процессом округления.

Спок Groovy Тест

void 'test BigDecimal rounding'() {
    given:
    BigDecimal decimal = new BigDecimal(Integer.MAX_VALUE - 1.99)
    BigDecimal hugeDecimal = new BigDecimal(Integer.MAX_VALUE + 1.99)
    BigDecimal reallyHuge = new BigDecimal("10000000000000000000000000000000000000000000000")
    String decimalAsBigIntString = decimal.toBigInteger().toString()
    String hugeDecimalAsBigIntString = hugeDecimal.toBigInteger().toString()
    String reallyHugeAsBigIntString = reallyHuge.toBigInteger().toString()

    expect: 'decimals that can be truncated within Integer range to do so without exception'
    //GOOD: Truncates without exception
    '' + decimal.intValue() == decimalAsBigIntString
    //BAD: Throws ArithmeticException 'Non-zero decimal digits' because we lose information
    // decimal.intValueExact() == decimalAsBigIntString
    //GOOD: Truncates without exception
    '' + decimal.setScale(0, RoundingMode.DOWN).intValueExact() == decimalAsBigIntString

    and: 'truncated decimal that cannot be truncated within Integer range throw conversionOverflow exception'
    //BAD: hugeDecimal.intValue() is -2147483648 instead of 2147483648
    //'' + hugeDecimal.intValue() == hugeDecimalAsBigIntString
    //BAD: Throws ArithmeticException 'Non-zero decimal digits' because we lose information
    //'' + hugeDecimal.intValueExact() == hugeDecimalAsBigIntString
    //GOOD: Throws conversionOverflow ArithmeticException because to large
    //'' + hugeDecimal.setScale(0, RoundingMode.DOWN).intValueExact() == hugeDecimalAsBigIntString

    and: 'truncated decimal that cannot be truncated within Integer range throw conversionOverflow exception'
    //BAD: hugeDecimal.intValue() is 0
    //'' + reallyHuge.intValue() == reallyHugeAsBigIntString
    //GOOD: Throws conversionOverflow ArithmeticException because to large
    //'' + reallyHuge.intValueExact() == reallyHugeAsBigIntString
    //GOOD: Throws conversionOverflow ArithmeticException because to large
    //'' + reallyHuge.setScale(0, RoundingMode.DOWN).intValueExact() == reallyHugeAsBigIntString

    and: 'if using Java 8, BigInteger has intValueExact() just like BigDecimal'
    //decimal.toBigInteger().intValueExact() == decimal.setScale(0, RoundingMode.DOWN).intValueExact()
}

Ответ 5

Следующее должно сделать трюк:

BigDecimal d = new BigDecimal(10);
int i = d.intValue();

Ответ 8

Я обнаружил, что это не работает для меня. Я вытаскивал значение ячейки из JTable, но не мог использовать double или int и т.д. Мое решение:

Object obj = getTable().getValueAt(row, 0);

где строка 0 всегда будет числом. Надеюсь, это поможет кому-то еще прокрутить!