Heim  >  Artikel  >  Java  >  Neue Währungs-API in Java 9

Neue Währungs-API in Java 9

伊谢尔伦
伊谢尔伦Original
2016-11-26 10:10:551444Durchsuche

JSR 354 definiert eine neue Java-Währungs-API, deren offizielle Einführung in Java 9 geplant ist. In diesem Artikel werfen wir einen Blick auf die Referenzimplementierung: den aktuellen Fortschritt von JavaMoney.

Genau wie mein vorheriger Artikel über die neue Datums- und Uhrzeit-API von Java 8 demonstriert dieser Artikel hauptsächlich die Verwendung der neuen API durch Code.

Bevor ich anfange, möchte ich einen Absatz verwenden, um den Zweck dieses neuen Satzes von APIs, die durch die Spezifikation definiert werden, kurz zusammenzufassen:

Für viele Anwendungen ist der Geldwert ein Schlüsselmerkmal. aber JDK bietet dafür fast keine Unterstützung. Genau genommen stellt die vorhandene Klasse java.util.Currency nur eine Datenstruktur der aktuellen ISO 4217-Währung dar, verfügt jedoch über keine zugehörigen Werte oder benutzerdefinierten Währungen. JDK verfügt außerdem über keine integrierte Unterstützung für Währungsoperationen und -umrechnungen, geschweige denn über einen Standardtyp, der Währungswerte darstellen kann.

Wenn Sie Maven verwenden, müssen Sie dem Projekt nur die folgende Referenz hinzufügen, um die aktuellen Funktionen der Referenzimplementierung zu erleben:

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

Die in der Spezifikation genannten Klassen und Schnittstellen sind unter dem Paket javax.money.*.

Beginnen wir mit den beiden Kernschnittstellen, CurrencyUnit und MonetaryAmount.

CurrencyUnit und MonetaryAmount

CurrencyUnit stellt die Währung dar. Sie ähnelt in gewisser Weise der aktuellen Klasse java.util.Currency, unterstützt jedoch benutzerdefinierte Implementierungen. Aus der Standarddefinition kann java.util.Currency diese Schnittstelle auch implementieren. Instanzen von „CurrencyUnit“ können über die MonetaryCurrencies-Fabrik abgerufen werden:

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

MontetaryAmount stellt den spezifischen Betrag einer bestimmten Währung dar. Normalerweise ist es an eine Währungseinheit gebunden.

MontetaryAmount ist wie auch CurrencyUnit eine Schnittstelle, die mehrere Implementierungen unterstützen kann.

Implementierungen von CurrencyUnit und MontetaryAmount müssen unveränderlich, threadsicher und vergleichbar sein.

/ 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 und FastMoney sind zwei Implementierungen von MonetaryAmount in der JavaMoney-Bibliothek. Money ist die Standardimplementierung, die BigDecimal zum Speichern von Beträgen verwendet. FastMoney ist eine optionale Implementierung, die zum Speichern von Beträgen den Typ „Long“ verwendet. Der Dokumentation zufolge sind Vorgänge auf FastMoney etwa 10 bis 15 Mal schneller als auf Money. Allerdings sind Betragsgröße und Präzision von FastMoney auf den Long-Typ beschränkt.

Beachten Sie, dass Money und FastMoney hier spezifische Implementierungsklassen sind (sie befinden sich unter dem Paket org.javamoney.moneta.*, nicht unter javax.money.*). Wenn Sie keinen bestimmten Typ angeben möchten, können Sie MonetaryAmountFactory verwenden, um eine Instanz von MonetaryAmount zu generieren:

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

Diese beiden MontetaryAmount-Instanzen gelten nur dann und nur dann als gleich, wenn die Implementierungsklasse Währung ist Einheit und Wert sind alle gleich.

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 enthält eine Fülle von Methoden, mit denen bestimmte Währungen, Beträge, Genauigkeiten usw. ermittelt werden können:

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;

Die Verwendung von MonetaryAmount

finden Sie unter Arithmetische Operationen auf 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);

Rundungsoperationen sind ein sehr wichtiger Teil der Betragsumrechnung. MonetaryAmount kann mit dem Rundungsoperator gerundet werden:

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

Hier werden 12,3456 US-Dollar gemäß den Standardrundungsregeln der aktuellen Währung umgerechnet.

Beim Betrieb der MonetaryAmount-Sammlung gibt es viele praktische Tools und Methoden, die zum Filtern, Sortieren und Gruppieren verwendet werden können. Diese Methoden können auch mit der Streams-API von Java 8 verwendet werden.

Sehen Sie sich die folgende Sammlung an:

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

Wir können den Betrag nach Währungseinheit filtern:

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

Wir können auch größere Beträge herausfiltern als oder kleiner als ein bestimmter Schwellenwert Der Betrag:

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

Die Sortierung ist ebenfalls ähnlich:

// 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());

Es gibt auch Gruppierungsoperationen:

// 按货币单位进行分组
// {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 Bietet auch eine Reduktionsfunktion, die verwendet werden kann, um den Maximalwert, den Minimalwert und die Summe zu erhalten:

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()); //

Benutzerdefinierte MonetaryAmount-Operation

MonetaryAmount bietet außerdem einen sehr benutzerfreundlichen Erweiterungspunkt namens MonetaryOperator . MonetaryOperator ist eine funktionale Schnittstelle, die einen MonetaryAmount-Eingabeparameter empfängt und ein neues MonetaryAmount-Objekt zurückgibt.

// 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

Standard-API-Funktionen werden über die MonetaryOperator-Schnittstelle implementiert. Beispielsweise wird die oben beschriebene Rundungsoperation in Form der MonetaryOperator-Schnittstelle bereitgestellt.

Wechselkurs

Der Wechselkurs kann über ExchangeRateProvider abgerufen werden. JavaMoney wird mit mehreren verschiedenen ExchangeRateProvider-Implementierungen geliefert. Die beiden wichtigsten sind ECBCurrentRateProvider und IMFRateProvider.

ECBCurrentRateProvider fragt die Daten der Europäischen Zentralbank (EZB) ab und IMFRateProvider fragt den Wechselkurs des Internationalen Währungsfonds (IWF) ab.

// 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");

Wenn ExchangeRateProvider nicht angegeben ist, wird CompoundRateProvider zurückgegeben. Der CompoundRateProvider delegiert Wechselkursumrechnungsanfragen an eine Kette von ExchangeRateProvider und gibt Daten vom ersten Anbieter zurück, der genaue Ergebnisse zurückgibt.

// 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

Währungsumrechnung

Die Umrechnung zwischen verschiedenen Währungen kann über die von ExchangeRateProvider zurückgegebenen „CurrencyConversions“ durchgeführt werden.

// 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)

Bitte beachten Sie, dass CurrencyConversion auch die MonetaryOperator-Schnittstelle implementiert. Wie andere Operationen kann es auch über die Methode MonetaryAmount.with() aufgerufen werden.

Formatieren und Parsen

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

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

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn