Java Money and the Currency API
1.概述
JSR 354 –"货币和金钱"解决了Java中货币和货币金额的标准化问题。
它的目标是向Java生态系统中添加灵活且可扩展的API,并使使用金额更简单,更安全。
JSR并未进入JDK 9,但已成为将来JDK版本的候选者。
2.设定
首先,让我们在pom.xml文件中定义依赖项:
1 2 3 4 5 | <dependency> <groupId>org.javamoney</groupId> <artifactId>moneta</artifactId> <version>1.1</version> </dependency> |
可以在此处检查依赖项的最新版本。
3. JSR-354的功能
"货币和金钱" API的目标:
提供用于处理和计算金额的API
定义代表货币和货币金额以及货币舍入的类
处理货币汇率
处理货币和货币量的格式和解析
4.型号
下图描述了JSR-354规范的主要类别:
该模型包含两个主要接口CurrencyUnit和MonetaryAmount,以下各节对此进行了说明。
5. CurrencyUnit
CurrencyUnit对货币的最小属性进行建模。 可以使用Monetary.getCurrency方法获取其实例:
1 2 3 4 5 6 7 8 9 | @Test public void givenCurrencyCode_whenString_thanExist() { CurrencyUnit usd = Monetary.getCurrency("USD"); assertNotNull(usd); assertEquals(usd.getCurrencyCode(),"USD"); assertEquals(usd.getNumericCode(), 840); assertEquals(usd.getDefaultFractionDigits(), 2); } |
我们使用货币的String表示形式创建CurrencyUnit,这可能会导致我们尝试创建不存在代码的货币的情况。 使用不存在的代码创建货币会引发UnknownCurrency异常:
1 2 3 4 | @Test(expected = UnknownCurrencyException.class) public void givenCurrencyCode_whenNoExist_thanThrowsError() { Monetary.getCurrency("AAA"); } |
6.货币金额
MonetaryAmount是货币金额的数字表示。 它始终与CurrencyUnit关联,并定义货币的货币表示形式。
金额可以以不同的方式实现,着重于每个具体用例定义的货币表示需求的行为。 例如。 Money和FastMoney是MonetaryAmount接口的实现。
FastMoney使用long作为数字表示形式来实现MonetaryAmount,并且以精度为代价比BigDecimal更快。 当我们需要性能且精度不是问题时,可以使用它。
可以使用默认工厂创建通用实例。 让我们展示获取MonetaryAmount实例的不同方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Test public void givenAmounts_whenStringified_thanEquals() { CurrencyUnit usd = Monetary.getCurrency("USD"); MonetaryAmount fstAmtUSD = Monetary.getDefaultAmountFactory() .setCurrency(usd).setNumber(200).create(); Money moneyof = Money.of(12, usd); FastMoney fastmoneyof = FastMoney.of(2, usd); assertEquals("USD", usd.toString()); assertEquals("USD 200", fstAmtUSD.toString()); assertEquals("USD 12", moneyof.toString()); assertEquals("USD 2.00000", fastmoneyof.toString()); } |
7.货币算术
我们可以在Money和FastMoney之间执行货币算术运算,但是当我们组合这两个类的实例时需要小心。
例如,当我们比较FastMoney的一个欧元实例和Money的一个欧元实例时,结果是它们不相同:
1 2 3 4 5 6 7 8 9 | @Test public void givenCurrencies_whenCompared_thanNotequal() { MonetaryAmount oneDolar = Monetary.getDefaultAmountFactory() .setCurrency("USD").setNumber(1).create(); Money oneEuro = Money.of(1,"EUR"); assertFalse(oneEuro.equals(FastMoney.of(1,"EUR"))); assertTrue(oneDolar.equals(Money.of(1,"USD"))); } |
我们可以使用MonetaryAmount类提供的方法执行加,减,乘,除和其他货币算术运算。
算术运算应抛出ArithmeticException,如果数量之间的算术运算优于所使用的数字表示类型的功能,例如,如果我们尝试将其除以三,则会得到ArithmeticException,因为结果是一个无穷大数:
1 2 3 4 5 6 | @Test(expected = ArithmeticException.class) public void givenAmount_whenDivided_thanThrowsException() { MonetaryAmount oneDolar = Monetary.getDefaultAmountFactory() .setCurrency("USD").setNumber(1).create(); oneDolar.divide(3); } |
在增加或减少金额时,最好使用作为MonetaryAmount实例的参数,因为我们需要确保两个金额使用相同的货币来执行金额之间的运算。
7.1。 计算金额
可以使用多种方法来计算总金额,一种方法是简单地将金额链接到:
1 2 3 4 5 6 7 8 9 10 11 12 | @Test public void givenAmounts_whenSummed_thanCorrect() { MonetaryAmount[] monetaryAmounts = new MonetaryAmount[] { Money.of(100,"CHF"), Money.of(10.20,"CHF"), Money.of(1.15,"CHF")}; Money sumAmtCHF = Money.of(0,"CHF"); for (MonetaryAmount monetaryAmount : monetaryAmounts) { sumAmtCHF = sumAmtCHF.add(monetaryAmount); } assertEquals("CHF 111.35", sumAmtCHF.toString()); } |
链接也可以应用于减法:
1 | Money calcAmtUSD = Money.of(1,"USD").subtract(fstAmtUSD); |
相乘:
1 | MonetaryAmount multiplyAmount = oneDolar.multiply(0.25); |
或除法:
1 | MonetaryAmount divideAmount = oneDolar.divide(0.25); |
让我们比较一下使用Strings的算术结果,并假设使用Strings,因为结果还包含货币:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @Test public void givenArithmetic_whenStringified_thanEqualsAmount() { CurrencyUnit usd = Monetary.getCurrency("USD"); Money moneyof = Money.of(12, usd); MonetaryAmount fstAmtUSD = Monetary.getDefaultAmountFactory() .setCurrency(usd).setNumber(200.50).create(); MonetaryAmount oneDolar = Monetary.getDefaultAmountFactory() .setCurrency("USD").setNumber(1).create(); Money subtractedAmount = Money.of(1,"USD").subtract(fstAmtUSD); MonetaryAmount multiplyAmount = oneDolar.multiply(0.25); MonetaryAmount divideAmount = oneDolar.divide(0.25); assertEquals("USD", usd.toString()); assertEquals("USD 1", oneDolar.toString()); assertEquals("USD 200.5", fstAmtUSD.toString()); assertEquals("USD 12", moneyof.toString()); assertEquals("USD -199.5", subtractedAmount.toString()); assertEquals("USD 0.25", multiplyAmount.toString()); assertEquals("USD 4", divideAmount.toString()); } |
8.货币舍入
货币舍入只不过是将精度不确定的金额转换为舍入金额。
我们将使用Monetary类提供的getDefaultRounding API进行转换。 默认舍入值由货币提供:
1 2 3 4 5 6 7 8 9 | @Test public void givenAmount_whenRounded_thanEquals() { MonetaryAmount fstAmtEUR = Monetary.getDefaultAmountFactory() .setCurrency("EUR").setNumber(1.30473908).create(); MonetaryAmount roundEUR = fstAmtEUR.with(Monetary.getDefaultRounding()); assertEquals("EUR 1.30473908", fstAmtEUR.toString()); assertEquals("EUR 1.3", roundEUR.toString()); } |
9.货币换算
货币转换是处理货币的重要方面。 不幸的是,这些转换具有多种不同的实现和用例。
该API专注于基于来源,目标货币和汇率的货币转换的常见方面。
可以将货币换算或汇率的获取参数化:
1 2 3 4 5 6 7 8 9 10 11 12 | @Test public void givenAmount_whenConversion_thenNotNull() { MonetaryAmount oneDollar = Monetary.getDefaultAmountFactory().setCurrency("USD") .setNumber(1).create(); CurrencyConversion conversionEUR = MonetaryConversions.getConversion("EUR"); MonetaryAmount convertedAmountUSDtoEUR = oneDollar.with(conversionEUR); assertEquals("USD 1", oneDollar.toString()); assertNotNull(convertedAmountUSDtoEUR); } |
转换始终与货币绑定。 通过将CurrencyConversion传递给金额的with方法,可以简单地转换MonetaryAmount。
10.货币格式
格式化允许访问基于java.util.Locale的格式。 与JDK相反,此API定义的格式化程序是线程安全的:
1 2 3 4 5 6 7 8 9 10 11 12 | @Test public void givenLocale_whenFormatted_thanEquals() { MonetaryAmount oneDollar = Monetary.getDefaultAmountFactory() .setCurrency("USD").setNumber(1).create(); MonetaryAmountFormat formatUSD = MonetaryFormats.getAmountFormat(Locale.US); String usFormatted = formatUSD.format(oneDollar); assertEquals("USD 1", oneDollar.toString()); assertNotNull(formatUSD); assertEquals("USD1.00", usFormatted); } |
在这里,我们使用预定义的格式并为我们的货币创建自定义格式。 使用MonetaryFormats类的方法格式,可以直接使用标准格式。 我们定义了自定义格式,设置了格式查询构建器的pattern属性。
和以前一样,因为结果中包含货币,所以我们使用Strings测试结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Test public void givenAmount_whenCustomFormat_thanEquals() { MonetaryAmount oneDollar = Monetary.getDefaultAmountFactory() .setCurrency("USD").setNumber(1).create(); MonetaryAmountFormat customFormat = MonetaryFormats.getAmountFormat(AmountFormatQueryBuilder. of(Locale.US).set(CurrencyStyle.NAME).set("pattern","00000.00 ¤").build()); String customFormatted = customFormat.format(oneDollar); assertNotNull(customFormat); assertEquals("USD 1", oneDollar.toString()); assertEquals("00001.00 US Dollar", customFormatted); } |
11.总结
在这篇快速文章中,我们介绍了Java Money&Currency JSR的基础知识。
货币值无处不在,并且Java提供了开始支持和处理货币值,算术或货币转换的功能。
与往常一样,您可以在Github上的文章中找到代码。