JSR 354 は、Java 9 で正式に導入される予定の新しい Java 通貨 API を定義します。この記事では、JavaMoney のリファレンス実装、つまり 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.* パッケージの下にあります。 。
2 つのコア インターフェイス CurrencyUnit と MonetaryAmount から始めましょう。
CurrencyUnitとMonetaryAmount
CurrencyUnitは通貨を表します。これは、カスタム実装をサポートしていることを除けば、現在の java.util.Currency クラスに似ています。仕様定義から判断すると、java.util.Currency もこのインターフェースを実装できます。 CurrencyUnit のインスタンスは、MonetaryCurrency ファクトリを通じて取得できます。
// 根据货币代码来获取货币单位 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 の 2 つの実装です。 Money はデフォルトの実装で、BigDecimal を使用して金額を保存します。 FastMoney は、long 型を使用して金額を格納するオプションの実装です。ドキュメントによると、FastMoney での操作は Money での操作よりも約 10 ~ 15 倍高速です。ただし、FastMoneyの金額サイズと精度はlong型に限定されます。
ここでの Money と FastMoney はどちらも特定の実装クラスであることに注意してください (これらは javax.money.* ではなく org.javamoney.moneta.* パッケージの下にあります)。特定の型を指定したくない場合は、MonetaryAmountFactory を使用して MonetaryAmount インスタンスを生成できます:
MonetaryAmount specAmount = MonetaryAmounts.getDefaultAmountFactory() .setNumber(123.45) .setCurrency("USD") .create();
これら 2 つの 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 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;
ここでは、12.3456 米ドルが、現在の通貨のデフォルトの四捨五入ルールに従って変換されます。
MonetaryAmount コレクションを操作する場合、フィルター、並べ替え、グループ化に使用できる実用的なツールやメソッドが多数あります。これらのメソッドは、Java 8 のストリーム API でも使用できます。
次のコレクションを見てください:
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);
CurrencyUnitに基づいて金額をフィルタリングできます:
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
特定のしきい値より大きいまたは小さい金額をフィルタリングすることもできます:
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 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());
MonetaryFunctions は、最大値、最小値、および合計を取得するために使用できるリダクション関数も提供します:
// 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());
カスタマイズされた MonetaryAmount 操作
MonetaryAmount は、MonetaryOperator と呼ばれる非常に使いやすい拡張ポイントも提供します。 MonetaryOperator は、MonetaryAmount 入力パラメータを受け取り、新しい MonetaryAmount オブジェクトを返す関数インターフェイスです。
// 按货币单位进行分组 // {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
標準 API 機能はすべて、MonetaryOperator インターフェイスを通じて実装されます。たとえば、前に示した丸め操作は、MonetaryOperator インターフェイスの形式で提供されます。
為替レート
通貨の為替レートはExchangeRateProviderを通じて取得できます。 JavaMoney には、複数の異なる ExchangeRateProvider 実装が付属しています。最も重要な 2 つは、ECBCurrentRateProvider と IMFRateProvider です。
ECBCurrentRateProviderは欧州中央銀行(ECB)のデータをクエリし、IMFRateProviderは国際通貨基金(国際通貨基金、IMF)の為替レートをクエリします。
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()); //
ExchangeRateProvider が指定されていない場合は、CompoundRateProvider が返されます。 CompoundRateProvider は、為替レート変換リクエストを ExchangeRateProvider のチェーンに委任し、正確な結果を返す最初のプロバイダーからデータを返します。
// 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
通貨変換
異なる通貨間の変換は、ExchangeRateProvider によって返される CurrencyConversions を通じて完了できます。
// 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");
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);
注意,这里的¤符号在模式串中是作为货币的占位符。