如何定制JSR-354的Money类型限制最大小数位数并拒绝超限?
实现方案:通过MonetaryContext + MathContext达成严格的4位小数限制
完全可以通过配置自定义的MonetaryContext并结合指定MathContext的舍入模式,实现你要求的严格小数位限制——不管是创建金额还是算术运算,只要小数位超过4位就直接失败,不会隐式舍入或扩展。
核心思路
关键在于两点:
- 给
MonetaryContext指定固定的小数位数(scale=4),明确金额的精度上限 - 将
MathContext的舍入模式设置为RoundingMode.UNNECESSARY,这样任何导致小数位超标的操作都会直接抛出ArithmeticException,而非自动调整
Kotlin代码实现示例
1. 定义全局的严格4位小数上下文
import javax.money.MonetaryContextBuilder import javax.money.Money import java.math.BigDecimal import java.math.RoundingMode import javax.money.CurrencyUnit // 自定义MonetaryContext:固定4位小数,超范围直接报错 val FOUR_DECIMAL_STRICT_CONTEXT = MonetaryContextBuilder.of(Money::class.java) .setScale(4) // 强制最多4位小数 .set(MathContext(10, RoundingMode.UNNECESSARY)) // 总精度设为10(足够覆盖大部分业务场景的整数位+4位小数),舍入模式设为"非必要则报错" .build()
2. 创建金额时的校验
创建金额时传入自定义上下文,超出4位小数会直接抛出异常:
// 合法场景:4位小数,正常创建 val validEur = Money.of(BigDecimal("200.2023"), CurrencyUnit.of("EUR"), FOUR_DECIMAL_STRICT_CONTEXT) // 非法场景:5位小数,抛出ArithmeticException try { val invalidEur = Money.of(BigDecimal("200.20234"), CurrencyUnit.of("EUR"), FOUR_DECIMAL_STRICT_CONTEXT) } catch (e: ArithmeticException) { // 在这里处理"小数位超限"的业务逻辑,比如返回参数错误提示 }
如果是解析字符串金额,同样可以通过指定上下文的工厂来实现校验:
val amountFactory = MonetaryAmounts.getDefaultAmountFactory(Money::class.java) .setContext(FOUR_DECIMAL_STRICT_CONTEXT) // 合法解析 val parsedValid = amountFactory.setCurrency("EUR").setNumber("150.1234").create() // 非法解析:抛出异常 try { val parsedInvalid = amountFactory.setCurrency("EUR").setNumber("150.12345").create() } catch (e: ArithmeticException) { // 处理解析错误 }
3. 算术运算的校验
所有基于该上下文的金额进行算术运算时,只要结果超出4位小数,都会直接报错:
val moneyA = Money.of(BigDecimal("100.1234"), "EUR", FOUR_DECIMAL_STRICT_CONTEXT) val moneyB = Money.of(BigDecimal("100.1234"), "EUR", FOUR_DECIMAL_STRICT_CONTEXT) // 合法运算:结果200.2468,4位小数 val validSum = moneyA.add(moneyB) // 非法运算:结果会产生5位小数,抛出ArithmeticException try { // 100.1234 + 100.1233 = 200.2467;乘以1.5得到300.37005,超4位小数 val invalidResult = moneyA.add(Money.of(BigDecimal("100.1233"), "EUR", FOUR_DECIMAL_STRICT_CONTEXT)) .multiply(BigDecimal("1.5")) } catch (e: ArithmeticException) { // 处理运算结果超限的错误 }
注意事项
- 确保你使用的JSR-354实现(比如主流的Moneta库)支持这些配置,Moneta完全兼容上述逻辑
MathContext的总精度值可以根据业务场景调整,只要保证整数位+4位小数不超过该精度即可(比如设为10足够覆盖大部分常规金额场景)
内容的提问来源于stack exchange,提问作者Ulrich Schuster
相关产品推荐
相关产品推荐

