Java Money og Currency API

1. Oversigt

JSR 354 - "Valuta og penge" adresserer standardiseringen af ​​valutaer og monetære beløb i Java.

Dets mål er at tilføje en fleksibel og udvidelig API til Java-økosystemet og gøre arbejdet med monetære beløb enklere og mere sikkert.

JSR kom ikke ind i JDK 9, men er en kandidat til fremtidige JDK-udgivelser.

2. Opsætning

Lad os først definere afhængigheden af ​​vores pom.xml fil:

 org.javamoney moneta 1.1 

Den seneste version af afhængigheden kan kontrolleres her.

3. JSR-354 Funktioner

Målene med API'en "Valuta og penge":

  • At give en API til håndtering og beregning af monetære beløb
  • At definere klasser, der repræsenterer valutaer og monetære beløb, samt monetær afrunding
  • At håndtere valutakurser
  • At håndtere formatering og parsing af valutaer og monetære beløb

4. Model

Hovedklasser i JSR-354 specifikationen er afbildet i følgende diagram:

Modellen har to hovedgrænseflader Valutaenhed og Pengebeløb, forklaret i de følgende afsnit.

5. Valutaenhed

Valutaenhed modellerer de minimale egenskaber for en valuta. Dens forekomster kan opnås ved hjælp af Monetary.getCurrency metode:

@Test offentlig ugyldighed givenCurrencyCode_whenString_thanExist () {CurrencyUnit usd = Monetary.getCurrency ("USD"); assertNotNull (usd); assertEquals (usd.getCurrencyCode (), "USD"); assertEquals (usd.getNumericCode (), 840); assertEquals (usd.getDefaultFractionDigits (), 2); }

Vi skaber Valutaenhed ved hjælp af en Snor repræsentation af valutaen, kan dette føre til en situation, hvor vi forsøger at skabe en valuta med ikke-eksisterende kode. Oprettelse af valutaer med ikke-eksisterende koder hæver en Ukendt Valuta undtagelse:

@Test (forventet = UnknownCurrencyException.class) offentlig ugyldighed givenCurrencyCode_whenNoExist_thanThrowsError () {Monetary.getCurrency ("AAA"); } 

6. Monetært beløb

Monetært beløb er en numerisk repræsentation af et monetært beløb. Det er altid forbundet med Valutaenhed og definerer en monetær repræsentation af en valuta.

Beløbet kan implementeres på forskellige måder, idet man fokuserer på adfærden i en monetær repræsentationskrav, defineret af hver konkret brugssag. For eksempel. Penge og FastPenge er implementeringer af Monetært beløb interface.

FastPenge redskaber Monetært beløb ved brug af lang som numerisk repræsentation og er hurtigere end BigDecimal på bekostning af præcision det kan bruges, når vi har brug for ydeevne, og præcision er ikke et problem.

En generisk forekomst kan oprettes ved hjælp af en standardfabrik. Lad os vise den anden måde at opnå Monetært beløb tilfælde:

@Test offentlig ugyldighed 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. Monetær aritmetik

Vi kan udføre monetær aritmetik imellem Penge og FastPenge men vi skal være forsigtige, når vi kombinerer forekomster af disse to klasser.

For eksempel når vi sammenligner en Euro-forekomst af FastPenge med en euroinstans af Penge resultatet er, at de ikke er de samme:

@Test offentlig ugyldighed givenCurrency_whenCompared_thanNotequal () {MonetaryAmount oneDolar = Monetary.getDefaultAmountFactory () .setCurrency ("USD"). SetNumber (1) .create (); Penge oneEuro = Money.of (1, "EUR"); assertFalse (oneEuro.equals (FastMoney.of (1, "EUR")); assertTrue (oneDolar.equals (Money.of (1, "USD")); }

Vi kan udføre add, subtrahere, multiplicere, dele og andre monetære aritmetiske operationer ved hjælp af metoderne leveret af Monetært beløb klasse.

Aritmetiske operationer skal kaste et Aritmetisk undtagelse, hvis de aritmetiske operationer mellem beløb overgår egenskaberne for den anvendte numeriske repræsentationstype, for eksempel hvis vi forsøger at dividere en efter tre, får vi en Aritmetisk undtagelse fordi resultatet er et uendeligt antal:

@Test (forventet = ArithmeticException.class) offentlig ugyldighed givenAmount_whenDivided_thanThrowsException () {MonetaryAmount oneDolar = Monetary.getDefaultAmountFactory () .setCurrency ("USD"). SetNumber (1) .create (); oneDolar.divide (3); }

Når du tilføjer eller trækker beløb, er det bedre at bruge parametre, der er forekomster af Monetært beløb, da vi skal sikre, at begge beløb har samme valuta til at udføre operationer mellem beløb.

7.1. Beregning af beløb

I alt beløb kan beregnes på flere måder, en måde er simpelthen at kæde beløbene med:

@Test offentligt ugyldigt givenAmounts_whenSummed_thanCorrect () {MonetaryAmount [] monetaryAmounts = new MonetaryAmount [] {Money.of (100, "CHF"), Money.of (10.20, "CHF"), Money.of (1.15, "CHF") }; Penge sumAmtCHF = Money.of (0, "CHF"); for (MonetaryAmount monetaryAmount: monetaryAmounts) {sumAmtCHF = sumAmtCHF.add (monetaryAmount); } assertEquals ("CHF 111,35", sumAmtCHF.toString ()); }

Kæde kan også anvendes til fratrækning:

Penge calcAmtUSD = Money.of (1, "USD"). Trække (fstAmtUSD); 

Multiplikation:

MonetaryAmount multiplyAmount = oneDolar.multiply (0,25);

Eller opdeling:

MonetaryAmount divideAmount = oneDolar.divide (0,25);

Lad os sammenligne vores aritmetiske resultater ved hjælp af Strings, givet det med Strings, fordi resultatet også indeholder valutaen:

@Test offentlig ugyldighed 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 (); Penge subtraheretAmount = Money.of (1, "USD"). Fratræk (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 ("-199,5 USD", trukketAmount.toString ()); assertEquals ("0,25 USD", multiplyAmount.toString ()); assertEquals ("USD 4", divideAmount.toString ()); }

8. Monetær afrunding

Monetær afrunding er intet andet end en konvertering fra et beløb med en ubestemt præcision til et afrundet beløb.

Vi bruger getDefaultRounding API leveret af Monetær klasse for at foretage konverteringen. Standard afrundingsværdierne leveres af valutaen:

@Test offentlig ugyldighed 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. Valutakonvertering

Valutakonvertering er et vigtigt aspekt ved håndtering af penge. Desværre har disse konverteringer en lang række forskellige implementeringer og brugssager.

API'en fokuserer på de fælles aspekter af valutakonvertering baseret på kilde, målvaluta og valutakurs.

Valutakonvertering eller adgang til valutakurser kan parametriseres:

@Test offentlig ugyldighed givenAmount_whenConversion_thenNotNull () {MonetaryAmount oneDollar = Monetary.getDefaultAmountFactory (). SetCurrency ("USD") .setNumber (1) .create (); CurrencyConversion conversionEUR = MonetaryConversions.getConversion ("EUR"); MonetaryAmount convertAmountUSDtoEUR = oneDollar.with (conversionEUR); assertEquals ("USD 1", oneDollar.toString ()); assertNotNull (convertAmountUSDtoEUR); }

En konvertering er altid bundet til valuta. Monetært beløb kan simpelthen konverteres ved at videresende en Valuta Omregning til beløbet med metode.

10. Valutaformatering

Formateringen giver adgang til formater baseret på java.util.Locale. I modsætning til JDK er formaterne defineret af denne API trådsikre:

@Test offentlig ugyldighed 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", usFormateret); }

Her bruger vi det foruddefinerede format og opretter et brugerdefineret format til vores valutaer. Brugen af ​​standardformatet er ligetil ved hjælp af metodeformatet for Monetære formater klasse. Vi definerede vores brugerdefinerede format, der indstillede mønsteregenskaben for formatforespørgselsbyggeren.

Som før, fordi valutaen er inkluderet i resultatet, tester vi vores resultater ved hjælp af Strenge:

@Test offentlig ugyldighed givenAmount_whenCustomFormat_thanEquals () {MonetaryAmount oneDollar = Monetary.getDefaultAmountFactory () .setCurrency ("USD"). SetNumber (1) .create (); MonetaryAmountFormat customFormat = MonetaryFormats.getAmountFormat (AmountFormatQueryBuilder. Af (Locale.US). Sæt (CurrencyStyle.NAME). Sæt ("mønster", "00000,00 ¤"). Build ()); Streng customFormatted = customFormat.format (oneDollar); assertNotNull (customFormat); assertEquals ("USD 1", oneDollar.toString ()); assertEquals ("00001,00 US Dollar", tilpasset formateret); }

11. Resumé

I denne hurtige artikel har vi dækket det grundlæggende i Java Money & Currency JSR.

Monetære værdier bruges overalt, og Java giver begynder at understøtte og håndtere monetære værdier, aritmetik eller valutakonvertering.

Som altid kan du finde koden fra artiklen på Github.