1. 概要

JSR 354 –「Currencyand Money」は、Javaでの通貨と金額の標準化に対応しています。

その目標は、Javaエコシステムに柔軟で拡張可能なAPIを追加し、金額をより簡単かつ安全に処理できるようにすることです。

JSRはJDK9には組み込まれていませんが、将来のJDKリリースの候補です。

2. 設定

まず、pom.xmlファイルへの依存関係を定義しましょう。

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

依存関係の最新バージョンはここで確認できます。

3. JSR-354の機能

「通貨とお金」APIの目標:

  • 金額を処理および計算するためのAPIを提供する
  • 通貨と金額、および金額の丸めを表すクラスを定義する
  • 為替レートに対処するには
  • 通貨と金額のフォーマットと解析を処理するため

4. モデル

JSR-354仕様の主なクラスは、次の図に示されています。

このモデルは、2つの主要なインターフェース CurrencyUnitMonetaryAmount、を保持します。これについては次のセクションで説明します。

5. CurrencyUnit

CurrencyUnit は、通貨の最小限のプロパティをモデル化します。 そのインスタンスは、Money.getCurrencyメソッドを使用して取得できます。

@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例外が発生します。

@Test(expected = UnknownCurrencyException.class)
public void givenCurrencyCode_whenNoExist_thanThrowsError() {
    Monetary.getCurrency("AAA");
}

6. MoneyAmount

MoneyAmount は、金額を数値で表したものです。 これは常にCurrencyUnitに関連付けられており、通貨の金銭的表現を定義します。

金額は、具体的なユースケースごとに定義された金銭的表現の要件の動作に焦点を当てて、さまざまな方法で実装できます。 例えば。 MoneyおよびFastMoneyは、MoneyAmountインターフェースの実装です。

FastMoney は、数値表現としてlongを使用してMonetaryAmountを実装し、精度を犠牲にしてBigDecimalよりも高速です。 パフォーマンスが必要な場合に使用でき、精度は問題ではありません。

汎用インスタンスは、デフォルトのファクトリを使用して作成できます。 MoneyAmountインスタンスを取得するさまざまな方法を示しましょう。

@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 。 金銭的算術

MoneyFastMoneyの間で金銭計算を実行できますが、これら2つのクラスのインスタンスを組み合わせる場合は注意が必要です。

たとえば、 FastMoneyの1つのユーロインスタンスをMoney の1つのユーロインスタンスと比較すると、結果は同じではありません。

@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")));
}

MoneyaryAmount クラスが提供するメソッドを使用して、加算、減算、乗算、除算、およびその他の金銭的算術演算を実行できます。

算術演算はArithmeticExceptionをスローする必要があります。たとえば、金額間の算術演算が使用される数値表現タイプの機能を上回っている場合、たとえば1を3で除算しようとすると、ArithmeticExceptionが発生します。 ]結果は無限の数であるため:

@Test(expected = ArithmeticException.class)
public void givenAmount_whenDivided_thanThrowsException() {
    MonetaryAmount oneDolar = Monetary.getDefaultAmountFactory()
      .setCurrency("USD").setNumber(1).create();
    oneDolar.divide(3);
}

金額を加算または減算するときは、 MoneyAmount のインスタンスであるパラメーターを使用することをお勧めします。これは、金額間の操作を実行するために両方の金額が同じ通貨であることを確認する必要があるためです。

7.1. 金額の計算

合計金額は複数の方法で計算できます。1つの方法は、金額を次のように連鎖させることです。

@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());
}

連鎖は、減算にも適用できます。

Money calcAmtUSD = Money.of(1, "USD").subtract(fstAmtUSD);

乗算:

MonetaryAmount multiplyAmount = oneDolar.multiply(0.25);

または分割:

MonetaryAmount divideAmount = oneDolar.divide(0.25);

結果には通貨も含まれているため、文字列を使用して算術結果を比較してみましょう。

@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. 端数処理

金額の四捨五入は、精度が不明な金額から四捨五入された金額への換算に他なりません。

Moneyクラスによって提供されるgetDefaultRoundingAPIを使用して変換を行います。 デフォルトの丸め値は、通貨によって提供されます。

@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は、ソース、ターゲット通貨、および為替レートに基づく通貨変換の一般的な側面に焦点を当てています。

通貨換算または為替レートへのアクセスは、パラメータ化できます。

@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);
}

変換は常に通貨にバインドされます。 MoneyAmount は、CurrencyConversionを金額のwithメソッドに渡すことで簡単に変換できます。

10. 通貨のフォーマット

フォーマットにより、java.util.Localeに基づくフォーマットにアクセスできます。 JDKとは異なり、このAPIで定義されているフォーマッターはスレッドセーフです。

@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);
}

ここでは、事前定義された形式を使用して、通貨のカスタム形式を作成しています。 MoneyFormats クラスのメソッド形式を使用すると、標準形式を簡単に使用できます。 フォーマットクエリビルダーのパターンプロパティを設定して、カスタムフォーマットを定義しました。

以前と同様に、通貨が結果に含まれているため、Stringsを使用して結果をテストします。

@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&CurrencyJSRの基本について説明しました。

金銭的価値はどこでも使用されており、Javaが提供するものは、金銭的価値、算術、または通貨変換のサポートと処理を開始しています。

いつものように、Githubの記事からコードを見つけることができます。