Java Money与Currency API浅谈


声明:本文转载自https://my.oschina.net/VILLE/blog/1611920,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

一、概述

JSR 354 - “金钱和货币”解决了Java中货币和货币金额的标准化问题。

它的目标是为Java生态系统添加一个灵活的可扩展的API,并使货币量更简单,更安全。

JSR没有进入JDK 9,而是未来JDK版本的候选人。虽然在Java9有Currency类简单实现但是实际开发中满足不了需求

二、引入

在maven的pom.xml中做如下引入

      <dependency>         <groupId>org.javamoney</groupId>         <artifactId>moneta</artifactId>         <version>最新版本</version>      </dependency> 

在grade中

    compile group: 'org.javamoney', name: 'moneta', version: '最新版本' 

最新版本依赖,可以查看,点击这里

三、JSR-354功能

“货币和金钱”API的目标:

1.提供处理和计算货币金额的API

2.定义货币和货币金额的类别,以及货币四舍五入

3.处理汇率

4.处理货币和货币金额的格式化和解析

四、API分析与使用

1.规范中提到的类及接口都在javax.money.*包下面。

2.先从核心的两个接口CurrencyUnit与MonetaryAmount开始剖析

3.CurrencyUnit及MonetaryAmount

CurrencyUnit

代表的是货币。它有点类似于现在的java.util.Currency类,不同之处在于它支持自定义的实现。从规范的定义来看,java.util.Currency也是可以实现该接口的。CurrencyUnit的实例可以通过Monetary.getCurrency()方法获取,如下:

    //据货币代码来获取货币单位             CurrencyUnit currencyUnit = Monetary.getCurrency("USD");    //亦或根据国家及地区来获取货币单位    CurrencyUnit   unit    = Monetary.getCurrency(Locale.CANADA); 

CurrencyUnit模拟货币的最小属性,我们使用货币的字符串表示形式创建CurrencyUnit ,这可能会导致我们尝试使用不存在的代码创建货币的情况。使用不存在的代码创建货币会引发UnknownCurrency异常。

MonetaryAmount

MonetaryAmount是货币金额的数字表示。它始终与CurrencyUnit 关联,并定义货币的货币表示形式。

金额可以用不同的方式来实现,重点放在由每个具体用例所定义的货币表示要求的行为上。例如。Money和FastMoney是MonetaryAmount接口的实现。

FastMoney实现MONETARYAMOUNT使用长为数字表示,并且比更快的BigDecimal在精度的成本; 它可以在我们需要性能时使用,精度不是问题。

Money与FastMoney是JavaMoney库中MonetaryAmount的两种实现。Money是默认实现,它使用BigDecimal来存储金额。FastMoney是可选的另一个实现,它用long类型来存储金额。根据文档来看,FastMoney上的操作要比Money的快10到15倍左右。然而,FastMoney的金额大小与精度都受限于long类型。

注意:这里的Money和FastMoney都是具体的实现类(它们在org.javamoney.moneta.包下面,而不是javax.money.)。如果你不希望指定具体类型的话,可以通过MonetaryAmountFactory来生成一个MonetaryAmount的实例

通用实例可以使用默认工厂创建。

e.g:

    CurrencyUnit currencyUnit = Monetary.getCurrency(Locale.US);          //金额表示     MonetaryAmount fstAmtUSD = Monetary.getDefaultAmountFactory().setCurrency(currencyUnit).setNumber(200).create();          Money money = Money.of(12, currencyUnit);          FastMoney fastMoney = FastMoney.of(2, currencyUnit); 

注意:当且仅当实现类,货币单位,以及数值全部相等时才认为这两个MontetaryAmount实例是相等的。

MonetaryAmount内包含丰富的方法,可以用来获取具体的货币,金额,精度等等。

    //货币计算     MonetaryAmount oneDolar = Monetary.getDefaultAmountFactory().setCurrency(currencyUnit).setNumber(1).create();     Money oneEuro = Money.of(1, "EUR");      //"+"     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);     }               //"-"      Money calcAmtUSD = Money.of(1, "USD").subtract(fstAmtUSD);            //"*"     MonetaryAmount multiplyAmount = oneDolar.multiply(0.25);         //"\"     MonetaryAmount divideAmount = oneDolar.divide(0.25);        Money moneyOf = Money.of(12, currencyUnit);    fstAmtUSD = Monetary.getDefaultAmountFactory().setCurrency(currencyUnit).setNumber(200.50).create();    oneDolar = Monetary.getDefaultAmountFactory().setCurrency("USD").setNumber(1).create();    Money subtractedAmount = Money.of(1, "USD").subtract(fstAmtUSD);    multiplyAmount = oneDolar.multiply(0.25);    divideAmount = oneDolar.divide(0.25);        //四舍五入    MonetaryAmount fstAmtEUR = Monetary.getDefaultAmountFactory().setCurrency("EUR").setNumber(1.30473908).create();    MonetaryAmount roundEUR = fstAmtEUR.with(Monetary.getDefaultRounding());        MonetaryAmount oneDollar = Monetary.getDefaultAmountFactory().setCurrency("USD").setNumber(1).create();        //货币格式化以及解析    MonetaryAmountFormat formatUSD = MonetaryFormats.getAmountFormat(Locale.US);    String usFormatted = formatUSD.format(oneDollar);    MonetaryAmount parsed = germanFormat.parse("12,4 USD"); 

可以通过AmountFormatQueryBuilder来生成自定义的格式。

   MonetaryAmountFormat customFormat = MonetaryFormats.getAmountFormat(    AmountFormatQueryBuilder.of(Locale.US).set(CurrencyStyle.NAME).set("pattern", "00,00,00,00.00 #").build()); 

注意,这里的#符号在模式串中是作为货币的占位符。

在操作在操作MonetaryAmount集合时,有许多实用的工具方法可以用来进行过滤,排序以及分组。这些方法还可以与Java 8的流API一起配套使用。

example:

    List<MonetaryAmount> amounts = new ArrayList<>();     amounts.add(Money.of(2000.00, "EUR"));     amounts.add(Money.of(4200.00, "USD"));     amounts.add(Money.of(700.00, "USD"));     amounts.add(Money.of(13.37, "JPY"));     amounts.add(Money.of(188000.80, "USD")); 

根据CurrencyUnit来进行金额过滤:

    CurrencyUnit yen = Monetary.getCurrency("JPY");          CurrencyUnit dollar = Monetary.getCurrency("USD");            // 根据货币过滤,只返回美金          List<MonetaryAmount> onlyDollar = amounts.stream().filter(MonetaryFunctions.isCurrency(dollar)).collect(Collectors.toList());        // 根据货币过滤,只返回美金和日元        List<MonetaryAmount> onlyDollarAndYen = amounts.stream().filter(MonetaryFunctions.isCurrency(dollar, yen)).collect(Collectors.toList()); 

还可以过滤出大于或小于某个阈值的金额:

MonetaryAmount tenDollar = Money.of(1000, dollar);  List<MonetaryAmount> greaterThanTenDollar = amounts.stream().filter(MonetaryFunctions.isCurrency(dollar))     .filter(MonetaryFunctions.isGreaterThan(tenDollar)).collect(Collectors.toList()); 

排序与分组

// Sorting dollar values by number value     List<MonetaryAmount> sortedByAmount = onlyDollar.stream().sorted(MonetaryFunctions.sortNumber()).collect(Collectors.toList());  //Sorting by CurrencyUnit   List<MonetaryAmount> sortedByCurrencyUnit = amounts.stream().sorted(MonetaryFunctions.sortCurrencyUnit()).collect(Collectors.toList());  //按货币单位进行分组    Map<CurrencyUnit, List<MonetaryAmount>> groupedByCurrency = amounts.stream().collect(MonetaryFunctions.groupByCurrencyUnit());  // 分组并进行汇总 Map<CurrencyUnit, MonetarySummaryStatistics> summary = amounts.stream().collect(MonetaryFunctions.groupBySummarizingMonetary()).get();  MonetarySummaryStatistics dollarSummary = summary.get(dollar); MonetaryAmount average = dollarSummary.getAverage(); MonetaryAmount min = dollarSummary.getMin(); MonetaryAmount max = dollarSummary.getMax(); MonetaryAmount sum = dollarSummary.getSum(); long count = dollarSummary.getCount(); 

MonetaryFunctions还提供了归约函数,可以用来获取最大值,最小值,以及求和:

List<MonetaryAmount> amounts = new ArrayList<>(); amounts.add(Money.of(10, "EUR")); amounts.add(Money.of(7.5, "EUR")); amounts.add(Money.of(12, "EUR"));  Optional<MonetaryAmount> max = amounts.stream().reduce(MonetaryFunctions.max()); // "EUR 12" Optional<MonetaryAmount> min = amounts.stream().reduce(MonetaryFunctions.min()); // "EUR 7.5" Optional<MonetaryAmount> sum = amounts.stream().reduce(MonetaryFunctions.sum()); // "EUR 29.5" 

自定义的MonetaryAmount操作

MonetaryAmount还提供了一个非常友好的扩展点叫作MonetaryOperator。MonetaryOperator是一个函数式接口,它接收一个MonetaryAmount入参并返回一个新的MonetaryAmount对象。

MonetaryOperator tenPercentOperator = (MonetaryAmount amount) -> {   BigDecimal baseAmount = amount.getNumber().numberValue(BigDecimal.class);   BigDecimal tenPercent = baseAmount.multiply(new BigDecimal("0.1"));   return Money.of(tenPercent, amount.getCurrency()); };  MonetaryAmount dollars = Money.of(12.34567, "USD");  MonetaryAmount tenPercentDollars = dollars.with(tenPercentOperator); 

标准的API特性都是通过MonetaryOperator的接口来实现的。比方说,前面看到的舍入操作就是以MonetaryOperator接口的形式来提供的。

五、汇率

货币兑换率可以通过ExchangeRateProvider来获取。JavaMoney自带了多个不同的ExchangeRateProvider的实现。其中最重要的两个是ECBCurrentRateProvider与 IMFRateProvider。

ECBCurrentRateProvider查询的是欧洲中央银行(European Central Bank,ECB)的数据而IMFRateProvider查询的是国际货币基金组织(International Monetary Fund,IMF)的汇率。

e.g:

//get the default ExchangeRateProvider (CompoundRateProvider)   ExchangeRateProvider exchangeRateProvider = MonetaryConversions.getExchangeRateProvider();  // get the names of the default provider chain // [IDENT, ECB, IMF, ECB-HIST] List<String> defaultProviderChain = MonetaryConversions.getDefaultProviderChain();  // get a specific ExchangeRateProvider (here ECB) ExchangeRateProvider ecbExchangeRateProvider = MonetaryConversions.getExchangeRateProvider("ECB"); 

如果没有指定ExchangeRateProvider的话返回的就是CompoundRateProvider。CompoundRateProvider会将汇率转换请求委派给一个ExchangeRateProvider链并将第一个返回准确结果的提供商的数据返回。

ExchangeRate rate = exchangeRateProvider.getExchangeRate("EUR", "USD");  NumberValue factor = rate.getFactor(); // 1.2537 (at time writing) CurrencyUnit baseCurrency = rate.getBaseCurrency(); // EUR CurrencyUnit targetCurrency = rate.getCurrency(); // USD 

六、币种转换

不同货币间的转换可以通过ExchangeRateProvider返回的CurrencyConversions来完成。

CurrencyConversion dollarConversion = MonetaryConversions.getConversion("USD");  CurrencyConversion ecbDollarConversion = ecbExchangeRateProvider.getCurrencyConversion("USD");  MonetaryAmount tenEuro = Money.of(10, "EUR");  MonetaryAmount inDollar = tenEuro.with(dollarConversion); 

注意:CurrencyConversion也实现了MonetaryOperator接口。正如其它操作一样,它也能通过MonetaryAmount.with()方法来调用。

七、类结构图

输入图片说明

本文发表于2018年01月23日 20:32
(c)注:本文转载自https://my.oschina.net/VILLE/blog/1611920,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2362 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1