Wang Java dan API Mata Wang

1. Gambaran keseluruhan

JSR 354 - "Mata Wang dan Wang" menangani penyeragaman mata wang dan jumlah wang di Jawa.

Tujuannya adalah untuk menambahkan API yang fleksibel dan dapat diperluas ke ekosistem Java dan menjadikan kerja dengan jumlah wang lebih mudah dan selamat.

JSR tidak berjaya memasuki JDK 9 tetapi merupakan calon untuk pelepasan JDK yang akan datang.

2. Persediaan

Pertama, mari tentukan kebergantungan ke fail pom.xml kami :

 org.javamoney moneta 1.1  

Versi ketergantungan terkini boleh diperiksa di sini.

3. Ciri-ciri JSR-354

Matlamat API "Mata Wang dan Wang":

  • Untuk menyediakan API untuk mengendalikan dan mengira jumlah wang
  • Untuk menentukan kelas yang mewakili mata wang dan jumlah wang, serta pembundaran monetari
  • Untuk menangani kadar pertukaran mata wang
  • Untuk menangani pemformatan dan penguraian mata wang dan jumlah wang

4. Model

Kelas utama spesifikasi JSR-354, digambarkan dalam rajah berikut:

Model ini mempunyai dua antara muka utama CurrencyUnit dan MonetaryAmount, yang dijelaskan dalam bahagian berikut.

5. Mata WangUnit

CurrencyUnit memodelkan sifat minimum mata wang. Contohnya dapat diperoleh dengan menggunakan kaedah Monetary.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); }

Kami membuat CurrencyUnit menggunakan String yang mewakili mata wang, ini boleh menyebabkan situasi di mana kami cuba membuat mata wang dengan kod yang tidak ada. Membuat mata wang dengan kod yang tidak ada meningkatkan pengecualian UnknownCurrency :

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

6. Jumlah Wang

MonetaryAmount adalah perwakilan berangka dari jumlah wang. Ia selalu dikaitkan dengan CurrencyUnit dan menentukan perwakilan monetari mata wang.

Jumlah tersebut dapat dilaksanakan dengan cara yang berbeda, dengan fokus pada tingkah laku syarat perwakilan wang, yang ditentukan oleh setiap kes penggunaan konkrit. Sebagai contoh. Money and FastMoney adalah pelaksanaan antara muka MonetaryAmount .

FastMoney alat MonetaryAmount menggunakan lama sebagai perwakilan angka, dan adalah lebih cepat daripada BigDecimal pada kos ketepatan; ia boleh digunakan ketika kita memerlukan prestasi dan ketepatan tidak menjadi masalah.

Contoh generik boleh dibuat menggunakan kilang lalai. Mari tunjukkan cara berbeza untuk mendapatkan contoh MonetaryAmount :

@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 . Aritmetik Kewangan

Kita boleh melakukan aritmetik wang antara Wang dan FastMoney tetapi kita perlu berhati-hati ketika menggabungkan contoh kedua kelas ini.

Contohnya, apabila kita membandingkan satu contoh FastMoney Euro dengan satu wang Wang Euro hasilnya adalah bahawa mereka tidak sama:

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

Kita boleh melakukan operasi tambah, tolak, darab, bahagi dan aritmetik monetari lain menggunakan kaedah yang disediakan oleh kelas MonetaryAmount .

Operasi aritmetik harus membuang ArithmeticException , jika operasi aritmetik antara jumlah melebihi kemampuan jenis perwakilan angka yang digunakan, misalnya, jika kita cuba membahagikan satu dengan tiga, kita akan mendapat ArithmeticException kerana hasilnya adalah nombor yang tidak terbatas:

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

Semasa menambahkan atau mengurangkan jumlah, lebih baik menggunakan parameter yang merupakan contoh MonetaryAmount , kerana kita perlu memastikan bahawa kedua-dua jumlah tersebut mempunyai mata wang yang sama untuk melakukan operasi antara jumlah.

7.1. Mengira Jumlah

Sejumlah jumlah dapat dikira dalam pelbagai cara, satu cara adalah dengan mengaitkan jumlahnya dengan:

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

Rantai juga dapat digunakan untuk mengurangkan:

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

Melipatgandakan:

MonetaryAmount multiplyAmount = oneDolar.multiply(0.25);

Atau membahagikan:

MonetaryAmount divideAmount = oneDolar.divide(0.25);

Let's compare our arithmetic results using Strings, given that with Strings because the result also contains the currency:

@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 Rounding

Monetary rounding is nothing else than a conversion from an amount with an undetermined precision to a rounded amount.

We'll use the getDefaultRounding API provided by the Monetary class to make the conversion. The default rounding values are provided by the currency:

@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. Currency Conversion

Currency conversion is an important aspect of dealing with money. Unfortunately, these conversions have a great variety of different implementations and use cases.

The API focuses on the common aspects of currency conversion based on the source, target currency, and exchange rate.

Currency conversion or the access of exchange rates can be parametrized:

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

A conversion is always bound to currency. MonetaryAmount can simply be converted by passing a CurrencyConversion to the amount’s with method.

10. Currency Formatting

The formatting allows the access of formats based on java.util.Locale. Contrary to the JDK, the formatters defined by this API are thread-safe:

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

Here we're using the predefined format and creating a custom format for our currencies. The use of the standard format is straightforward using the method format of the MonetaryFormats class. We defined our custom format setting the pattern property of the format query builder.

As before because the currency is included in the result we test our results using 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. Ringkasan

Dalam artikel ringkas ini, kami telah membahas asas-asas JSR Wang & Mata Wang Java.

Nilai wang digunakan di mana-mana, dan Java menyediakan mulai menyokong dan menangani nilai monetari, aritmetik atau penukaran mata wang.

Seperti biasa, anda boleh mendapatkan kod dari artikel di Github.