搜索
首页Javajava教程Java 9中新的货币API

Java 9中新的货币API

Nov 26, 2016 am 10:10 AM
apijava

 JSR 354定义了一套新的Java货币API,计划会在Java 9中正式引入。本文中我们将来看一下它的参考实现:JavaMoney的当前进展。

  正如我在之前那篇Java 8新的日期时间API一文中那样,本文主要也是通过一些代码来演示下新的API的用法 。

  在开始之前,我想先用一段话来简短地总结一下规范定义的这套新的API的用意何在:

对许多应用而言货币价值都是一个关键的特性,但JDK对此却几乎没有任何支持。严格来讲,现有的java.util.Currency类只是代表了当前ISO 4217货币的一个数据结构,但并没有关联的值或者自定义货币。JDK对货币的运算及转换也没有内建的支持,更别说有一个能够代表货币值的标准类型了。

  如果你用的是Maven的话,只需把下面的引用添加到工里面便能够体验下该参考实现的当前功能了:

<dependency>
  <groupId>org.javamoney</groupId>
  <artifactId>moneta</artifactId>
  <version>0.9</version>
</dependency>

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

  我们先从核心的两个接口CurrencyUnit与MonetaryAmount开始讲起。

  CurrencyUnit及MonetaryAmount

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

// 根据货币代码来获取货币单位 CurrencyUnit euro = MonetaryCurrencies.getCurrency("EUR");
    CurrencyUnit usDollar = MonetaryCurrencies.getCurrency("USD"); // 根据国家及地区来获取货币单位
    CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN); CurrencyUnit
    canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA);

MontetaryAmount代表的是某种货币的具体金额。通常它都会与某个CurrencyUnit绑定。

  MontetaryAmount和CurrencyUnit一样,也是一个能支持多种实现的接口。

  CurrencyUnit与MontetaryAmount的实现必须是不可变,线程安全且可比较的。

/ get MonetaryAmount from CurrencyUnit
CurrencyUnit euro = MonetaryCurrencies.getCurrency("EUR");
MonetaryAmount fiveEuro = Money.of(5, euro);
  
// get MonetaryAmount from currency code
MonetaryAmount tenUsDollar = Money.of(10, "USD");
  
// FastMoney is an alternative MonetaryAmount factory that focuses on performance
MonetaryAmount sevenEuro = FastMoney.of(7, euro);

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

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

MonetaryAmount specAmount = MonetaryAmounts.getDefaultAmountFactory()
                .setNumber(123.45) .setCurrency("USD") .create();

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

MonetaryAmount oneEuro = Money.of(1, MonetaryCurrencies.getCurrency("EUR"));
boolean isEqual = oneEuro.equals(Money.of(1, "EUR")); // true
boolean isEqualFast = oneEuro.equals(FastMoney.of(1, "EUR")); // false

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

MonetaryAmount monetaryAmount = Money.of(123.45, euro);
CurrencyUnit currency = monetaryAmount.getCurrency();
NumberValue numberValue = monetaryAmount.getNumber();
  
int intValue = numberValue.intValue(); // 123
double doubleValue = numberValue.doubleValue(); // 123.45
long fractionDenominator = numberValue.getAmountFractionDenominator(); // 100
long fractionNumerator = numberValue.getAmountFractionNumerator(); // 45
int precision = numberValue.getPrecision(); // 5
  
// NumberValue extends java.lang.Number.
// So we assign numberValue to a variable of type Number
Number number = numberValue;

MonetaryAmount的使用

  可以在MonetaryAmount上进行算术运算: 

MonetaryAmount twelveEuro = fiveEuro.add(sevenEuro); // "EUR 12"
MonetaryAmount twoEuro = sevenEuro.subtract(fiveEuro); // "EUR 2"
MonetaryAmount sevenPointFiveEuro = fiveEuro.multiply(1.5); // "EUR 7.5"
  
// MonetaryAmount can have a negative NumberValue
MonetaryAmount minusTwoEuro = fiveEuro.subtract(sevenEuro); // "EUR -2"
  
// some useful utility methods
boolean greaterThan = sevenEuro.isGreaterThan(fiveEuro); // true
boolean positive = sevenEuro.isPositive(); // true
boolean zero = sevenEuro.isZero(); // false
  
// Note that MonetaryAmounts need to have the same CurrencyUnit to do mathematical operations
// this fails with: javax.money.MonetaryException: Currency mismatch: EUR/USD
fiveEuro.add(tenUsDollar);

舍入操作是金额换算里面非常重要的一部分。MonetaryAmount可以使用舍入操作符来进行四舍五入:

CurrencyUnit usd = MonetaryCurrencies.getCurrency("USD");
MonetaryAmount dollars = Money.of(12.34567, usd);
MonetaryOperator roundingOperator = MonetaryRoundings.getRounding(usd);
MonetaryAmount roundedDollars = dollars.with(roundingOperator); // USD 12.35

这里12.3456美金就会按当前货币默认的舍入规则来进行换算。

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

  看一下下面这个集合:

List<MonetaryAmount> amounts = new ArrayList<>();
amounts.add(Money.of(2, "EUR"));
amounts.add(Money.of(42, "USD"));
amounts.add(Money.of(7, "USD"));
amounts.add(Money.of(13.37, "JPY"));
amounts.add(Money.of(18, "USD"));

我们可以根据CurrencyUnit来进行金额过滤: 

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

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

MonetaryAmount tenDollar = Money.of(10, dollar);
  
// [USD 42, USD 18]
List<MonetaryAmount> greaterThanTenDollar = amounts.stream()
    .filter(MonetaryFunctions.isCurrency(dollar))
    .filter(MonetaryFunctions.isGreaterThan(tenDollar))
    .collect(Collectors.toList());

排序也是类似的:

// Sorting dollar values by number value
// [USD 7, USD 18, USD 42]
List<MonetaryAmount> sortedByAmount = onlyDollar.stream()
    .sorted(MonetaryFunctions.sortNumber())
    .collect(Collectors.toList());
  
// Sorting by CurrencyUnit
// [EUR 2, JPY 13.37, USD 42, USD 7, USD 18]
List<MonetaryAmount> sortedByCurrencyUnit = amounts.stream()
    .sorted(MonetaryFunctions.sortCurrencyUnit())
    .collect(Collectors.toList());

还有分组操作:

// 按货币单位进行分组
// {USD=[USD 42, USD 7, USD 18], EUR=[EUR 2], JPY=[JPY 13.37]}
Map<CurrencyUnit, List<MonetaryAmount>> groupedByCurrency = amounts.stream()
    .collect(MonetaryFunctions.groupByCurrencyUnit());
  
// 分组并进行汇总
Map<CurrencyUnit, MonetarySummaryStatistics> summary = amounts.stream()
    .collect(MonetaryFunctions.groupBySummarizingMonetary()).get();
  
// get summary for CurrencyUnit USD
MonetarySummaryStatistics dollarSummary = summary.get(dollar);
MonetaryAmount average = dollarSummary.getAverage(); // "USD 22.333333333333333333.."
MonetaryAmount min = dollarSummary.getMin(); // "USD 7"
MonetaryAmount max = dollarSummary.getMax(); // "USD 42"
MonetaryAmount sum = dollarSummary.getSum(); // "USD 67"
long count = dollarSummary.getCount(); // 3

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 7.5"
Optional<MonetaryAmount> min = amounts.stream().reduce(MonetaryFunctions.min()); // "EUR 12"
Optional<MonetaryAmount> sum = amounts.stream().reduce(MonetaryFunctions.sum()); //

自定义的MonetaryAmount操作

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

// A monetary operator that returns 10% of the input MonetaryAmount
// Implemented using Java 8 Lambdas
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");
  
// apply tenPercentOperator to MonetaryAmount
MonetaryAmount tenPercentDollars = dollars.with(tenPercentOperator); // USD 1.234567

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

  汇率

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

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

// 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链并将第一个返回准确结果的提供商的数据返回。

// get the exchange rate from euro to us dollar
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来完成。

// get the CurrencyConversion from the default provider chain
CurrencyConversion dollarConversion = MonetaryConversions.getConversion("USD");
  
// get the CurrencyConversion from a specific provider
CurrencyConversion ecbDollarConversion = ecbExchangeRateProvider.getCurrencyConversion("USD");
  
MonetaryAmount tenEuro = Money.of(10, "EUR");
  
// convert 10 euro to us dollar
MonetaryAmount inDollar = tenEuro.with(dollarConversion); // "USD 12.537" (at the time writing)

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

  格式化及解析

  MonetaryAmount可以通过MonetaryAmountFormat来与字符串进行解析/格式化。

// formatting by locale specific formats
MonetaryAmountFormat germanFormat = MonetaryFormats.getAmountFormat(Locale.GERMANY);
MonetaryAmountFormat usFormat = MonetaryFormats.getAmountFormat(Locale.CANADA);
  
MonetaryAmount amount = Money.of(12345.67, "USD");
  
String usFormatted = usFormat.format(amount); // "USD12,345.67"
String germanFormatted = germanFormat.format(amount); // 12.345,67 USD
  
// A MonetaryAmountFormat can also be used to parse MonetaryAmounts from strings
MonetaryAmount parsed = germanFormat.parse("12,4 USD");

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

// Creating a custom MonetaryAmountFormat
MonetaryAmountFormat customFormat = MonetaryFormats.getAmountFormat(
    AmountFormatQueryBuilder.of(Locale.US)
        .set(CurrencyStyle.NAME)
        .set("pattern", "00,00,00,00.00 ¤")
        .build());
  
// results in "00,01,23,45.67 US Dollar"
String formatted = customFormat.format(amount);

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

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
如何将Maven或Gradle用于高级Java项目管理,构建自动化和依赖性解决方案?如何将Maven或Gradle用于高级Java项目管理,构建自动化和依赖性解决方案?Mar 17, 2025 pm 05:46 PM

本文讨论了使用Maven和Gradle进行Java项目管理,构建自动化和依赖性解决方案,以比较其方法和优化策略。

如何使用适当的版本控制和依赖项管理创建和使用自定义Java库(JAR文件)?如何使用适当的版本控制和依赖项管理创建和使用自定义Java库(JAR文件)?Mar 17, 2025 pm 05:45 PM

本文使用Maven和Gradle之类的工具讨论了具有适当的版本控制和依赖关系管理的自定义Java库(JAR文件)的创建和使用。

如何使用咖啡因或Guava Cache等库在Java应用程序中实现多层缓存?如何使用咖啡因或Guava Cache等库在Java应用程序中实现多层缓存?Mar 17, 2025 pm 05:44 PM

本文讨论了使用咖啡因和Guava缓存在Java中实施多层缓存以提高应用程序性能。它涵盖设置,集成和绩效优势,以及配置和驱逐政策管理最佳PRA

如何将JPA(Java持久性API)用于具有高级功能(例如缓存和懒惰加载)的对象相关映射?如何将JPA(Java持久性API)用于具有高级功能(例如缓存和懒惰加载)的对象相关映射?Mar 17, 2025 pm 05:43 PM

本文讨论了使用JPA进行对象相关映射,并具有高级功能,例如缓存和懒惰加载。它涵盖了设置,实体映射和优化性能的最佳实践,同时突出潜在的陷阱。[159个字符]

Java的类负载机制如何起作用,包括不同的类载荷及其委托模型?Java的类负载机制如何起作用,包括不同的类载荷及其委托模型?Mar 17, 2025 pm 05:35 PM

Java的类上载涉及使用带有引导,扩展程序和应用程序类负载器的分层系统加载,链接和初始化类。父代授权模型确保首先加载核心类别,从而影响自定义类LOA

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能