Ответ 1
Вы бы назвали myBigDecimal.intValueExact()
(или просто intValue()
), и он даже выбросит исключение, если вы потеряете информацию. Это возвращает int, но autoboxing позаботится об этом.
У меня есть метод Hibernate, который возвращает мне BigDecimal. У меня есть другой метод API, которому нужно передать это число, но он принимает параметр Integer как параметр. Я не могу изменять типы возвращаемых данных или переменные типы обоих методов.
Теперь, как преобразовать BigDecimal в Integer и передать его второму методу?
Есть ли выход из этого?
Вы бы назвали myBigDecimal.intValueExact()
(или просто intValue()
), и он даже выбросит исключение, если вы потеряете информацию. Это возвращает int, но autoboxing позаботится об этом.
Можете ли вы гарантировать, что BigDecimal
никогда не будет содержать значение больше Integer.MAX_VALUE
?
Если да, то здесь ваш код вызывает intValue
:
Integer.valueOf(bdValue.intValue())
Хорошо, вы могли бы назвать BigDecimal.intValue()
:
Преобразует этот BigDecimal в int. Это преобразование аналогично сужению примитивного преобразования от двойного до короткого, как определено в Спецификации языка Java: любая дробная часть этого BigDecimal будет отброшена, и если полученный BigInteger слишком велик, чтобы вписаться в int, Возвращаются 32 бита. Обратите внимание, что это преобразование может потерять информацию об общей величине и точности этого значения BigDecimal, а также вернуть результат с противоположным знаком.
Затем вы можете либо явно вызвать Integer.valueOf(int)
, либо позволить автоматически делать бокс для вас, если вы используете достаточно недавнюю версию Java.
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()
}
Следующее должно сделать трюк:
BigDecimal d = new BigDecimal(10);
int i = d.intValue();
Пробовали ли вы вызов BigInteger # intValue()?
Я обнаружил, что это не работает для меня. Я вытаскивал значение ячейки из JTable, но не мог использовать double или int и т.д. Мое решение:
Object obj = getTable().getValueAt(row, 0);
где строка 0 всегда будет числом. Надеюсь, это поможет кому-то еще прокрутить!