Skip to main content

Java 9 is coming with money api

Posted by otaviojava on August 25, 2014 at 3:17 AM PDT

Since begin of the time, the man need to do exchanges of goods materials or services, began with simples goods exchanges. In this time, already has necessity to start the standardization process, the first step was using metals as gold, silver and bronze becoming to representative money today. Nowadays with large number of e-commerce and automation of finance market and another systems that take care of money. The question frequently done is: How take care of this important measure unit? This article will talk about this point.

The first question is, witch variable use to represent the money? Integer, Long, Double, Float or BigDecimal? In Java Effective's book, told the problem to use floating representation, so we should avoid float and double and use BigDecimal, long or int.

public class Product { 
private String name;
private BigDecimal price;
//getter and setter
}

Using BigDecimal, for example, we will have some problems, such show the value with monetary symbol, rounding, etc. So some developers make a MoneyUtils (a class with utilities of money), to help to do that. The problem will be spread and smell code. A good solution to solve this problem is create the Money's class, so centralize all money's behavior in just one object (show monetary symbol, etc.), this solutions look like more beautiful and has more OO aspect.

public class Product { 
private String name;
private Money price;
            //getter and setter
}

But with a strong globalization and the buys from the Internet, is important worry to another monetary units, beyond your country, so born new challenges:

  • Find rates
  • Store currency to each country
  • Exchange rate
  • Understand the world standardization to currencies (ISO 4217)

With this goal was born the money API, JSR 354, to make easy the money's manipulation such rounding, exchange rate, etc.

Starting to talk about this API, the first step is create the currency unit, it is represented using the CurrencyUnit's interface, it's possible make a instance some forms as inform the currency code or using java.util.Locale of country of origin.

public class CreateCurrencyUnit 
{
    public static void main( String[] args )
    {
        CurrencyUnit real = MonetaryCurrencies.getCurrency("BRL");
        CurrencyUnit dollar = MonetaryCurrencies.getCurrency(Locale.US);
        System.out.println(real); //BRL – the Brazilian currency
        System.out.println(dollar); //USD – the north American currency
    }
}

The next step is monetary value, this class has a strong relationship with a currency. The interface is MonetaryAmount and some implementations are:

Money: implementation that uses BigDecimal to represents the value.
FastMoney: this implementation uses long instead of BigDecimal, it is faster than Money, about 15 times, however has a limited decimals places, just five.
public class CreateMonetaryCurrency { 

public static void main(String[] args) {
CurrencyUnit real = MonetaryCurrencies.getCurrency("BRL");
CurrencyUnit dollar = MonetaryCurrencies.getCurrency(Locale.US);
        MonetaryAmount money = Money.of(120, real);
        MonetaryAmount fastMoney = FastMoney.of(80, dollar);
        System.out.println(money);
        System.out.println(fastMoney);
}
}

With MonetaryAmount created is possible do some operations:

public class SimpleOperations { 

public static void main(String[] args) {
CurrencyUnit dollar = MonetaryCurrencies.getCurrency(Locale.US);
        MonetaryAmount money = Money.of(120, dollar);
        MonetaryAmount money2 = Money.of(50, dollar);
        System.out.println(money.add(money2));
        System.out.println(money.subtract(money2));
        System.out.println(money.multiply(2));
        System.out.println(money.divide(2));
        System.out.println(money.isEqualTo(money2));
        System.out.println(money.isGreaterThan(money2));
        System.out.println(money.isGreaterThanOrEqualTo(money2));
        System.out.println(money.isGreaterThanOrEqualTo(money2));
        System.out.println(money.isLessThan(money2));
        System.out.println(money.isLessThanOrEqualTo(money2));
        System.out.println(money.isNegative());
        System.out.println(money.isNegativeOrZero());
}
}

Another possibility is format the money:

public class FormatExample { 

public static void main(String[] args) {
CurrencyUnit dollar = MonetaryCurrencies.getCurrency(Locale.US);

MonetaryAmount monetaryAmount = Money.of(1202.12D, dollar);
MonetaryAmountFormat germanFormat = MonetaryFormats.getAmountFormat(
Locale.GERMANY);
MonetaryAmountFormat usFormat = MonetaryFormats.getAmountFormat(
Locale.US);
MonetaryAmountFormat customFormat = MonetaryFormats.getAmountFormat(
                AmountFormatQueryBuilder.create(Locale.US).set(CurrencyStyle.SYMBOL).build());

System.out.println(germanFormat.format(monetaryAmount));//1.202,12 USD
System.out.println(usFormat.format(monetaryAmount));//USD1,202.12
System.out.println(customFormat.format(monetaryAmount));//$1,202.12


}
}

This API will come with Java 9, so all improvements, included in Java 8, such Stream, lambda, may be used, besides, to make easer the data's manipulation with Stream, was created the MonetaryFunction, that has methods utilities to manipulate MonetaryAmount, we can subdivided in 4 resources:

Reducer

Reducer operations such, the greatest value, lesser value, sum.

public class MonetaryReducerOperations { 

public static void main(String[] args) {
List moneys = StreamFactory.getDollars();
MonetaryAmount min = moneys.stream().reduce(MonetaryFunctions.min()).get();
MonetaryAmount max = moneys.stream().reduce(MonetaryFunctions.max()).get();
MonetaryAmount sum = moneys.stream().reduce(MonetaryFunctions.sum()).get();
System.out.println(min);//USD 50
System.out.println(max);//USD 120
System.out.println(sum);//USD 460
}

}

Filters

You may do selection to just one or more currency unit, beyond from a value (greater than, lesser, than, greater and equal than, between, etc.).

public class MonetaryFilterOperations { 
private static CurrencyUnit DOLLAR = MonetaryCurrencies.getCurrency(Locale.US);
private static CurrencyUnit EURO = MonetaryCurrencies.getCurrency("EUR");

public static void main(String[] args) {

MonetaryAmount money = Money.of(BigDecimal.valueOf(100D), DOLLAR);
MonetaryAmount min = Money.of(BigDecimal.valueOf(6D), DOLLAR);
MonetaryAmount max = Money.of(BigDecimal.valueOf(100D), DOLLAR);

List moneys = getMoneys();

List justDollar = moneys.stream()
.filter((MonetaryFunctions.isCurrency(DOLLAR)))
.collect(Collectors.toList());
List notEuro = moneys.stream().filter((MonetaryFunctions.isNotCurrency(EURO)))
.collect(Collectors.toList());
List euroOrDollar = moneys.stream().filter((MonetaryFunctions
.containsCurrencies(EURO, DOLLAR))).collect(Collectors.toList());
List dollarGreaterOneHundred = moneys.stream()
.filter((MonetaryFunctions.isCurrency(DOLLAR)
.and(MonetaryFunctions.isGreaterThan(money)))).collect(Collectors.toList());
List dollarGreaterOneHundredDistinct = moneys.stream().distinct()
.filter((MonetaryFunctions.isCurrency(DOLLAR)
.and(MonetaryFunctions.isGreaterThan(money)))).collect(Collectors.toList());
List between = moneys.stream()
.filter((MonetaryFunctions.isCurrency(DOLLAR)
.and(MonetaryFunctions.isBetween(min, max))))
.collect(Collectors.toList());

System.out.println(justDollar);
System.out.println(notEuro);
System.out.println(euroOrDollar);
System.out.println(dollarGreaterOneHundred);
System.out.println(dollarGreaterOneHundredDistinct);
System.out.println(between);

}


public static List getMoneys() {
List moneys = new ArrayList<>();
moneys.add(Money.of(120, DOLLAR));
moneys.add(Money.of(50, DOLLAR));
moneys.add(Money.of(80, DOLLAR));
moneys.add(Money.of(90, DOLLAR));
moneys.add(Money.of(120, DOLLAR));


moneys.add(Money.of(120, EURO));
moneys.add(Money.of(50, EURO));
moneys.add(Money.of(80, EURO));
moneys.add(Money.of(90, EURO));
moneys.add(Money.of(120, EURO));
return moneys;
}

Sort

Operation to do sort in a list or stream, so it's possible sort by CurrencyUnit or by value, in asc or desc way, and you can combine, in other words, I may sort by currency unit in ascending way and descending by value.

public class MonetarySorterOperations { 


public static void main(String[] args) {
List orderCurrency = getDollars().stream()
.sorted(MonetaryFunctions.sortCurrencyUnit())
.collect(Collectors.toList());
List orderSort = getDollars()
.stream()
.sorted(MonetaryFunctions.sortCurrencyUnit().thenComparing(
MonetaryFunctions.sortNumber()))
.collect(Collectors.toList());
List orderCurrencyNumber = getDollars()
.stream()
.sorted(MonetaryFunctions.sortCurrencyUnit().thenComparing(
MonetaryFunctions.sortCurrencyUnitDesc()))
.collect(Collectors.toList());

System.out.println(orderCurrency);
System.out.println(orderSort);
System.out.println(orderCurrencyNumber);

}

public static List getDollars() {
CurrencyUnit dollar = MonetaryCurrencies.getCurrency(Locale.US);
List moneys = new ArrayList<>();
moneys.add(Money.of(120, dollar));
moneys.add(Money.of(50, dollar));
moneys.add(Money.of(80, dollar));
moneys.add(Money.of(90, dollar));
moneys.add(Money.of(120, dollar));
return moneys;
}
}

Group

From Stream or list is possible create a summary (an object that contains informations as size of list, sum, lesser value, greater value and average), a map where the key is CurrencyUnit and the value is a list of MonetaryAmount and a map where the key is CurrencyUnit and the value is a summary of this currency unit.

public class MonetaryGroupOperations { 

private static CurrencyUnit DOLLAR = MonetaryCurrencies.getCurrency(Locale.US);
private static CurrencyUnit EURO = MonetaryCurrencies.getCurrency("EUR");


public static void main(String[] args) {
Map> groupBy = getCurrencies()
.stream().collect(MonetaryFunctions.groupByCurrencyUnit());
MonetarySummaryStatistics summary = getCurrencies().stream()
.filter(MonetaryFunctions.isCurrency(DOLLAR))
.collect(MonetaryFunctions.summarizingMonetary(DOLLAR));
GroupMonetarySummaryStatistics groupSummary = getCurrencies().stream()
.filter(MonetaryFunctions.isCurrency(DOLLAR))
.collect(MonetaryFunctions.groupBySummarizingMonetary());

System.out.println(groupBy);
System.out.println(summary.getMin());
System.out.println(summary.getMax());
System.out.println(summary.getAvarage());
System.out.println(summary.getCount());
System.out.println(groupSummary);
}

public static List getCurrencies() {


List moneys = new ArrayList<>();

moneys.add(Money.of(120, DOLLAR));
moneys.add(Money.of(50, DOLLAR));
moneys.add(Money.of(80, DOLLAR));
moneys.add(Money.of(90, DOLLAR));
moneys.add(Money.of(120, DOLLAR));

moneys.add(Money.of(120, EURO));
moneys.add(Money.of(50, EURO));
moneys.add(Money.of(80, EURO));
moneys.add(Money.of(90, EURO));
moneys.add(Money.of(120, EURO));
return moneys;
}
}

Exchange Rate

Another important resources is exchange rate, you can do that from ExchangeRateProvider's interface, there are two implementations:European Central Bank, the ECB, and International Monetary Fund, the IMF.

public class ExchangeExample { 

public static void main(String[] args) {
ExchangeRateProvider imfRateProvider = MonetaryConversions
.getExchangeRateProvider("IMF");
ExchangeRateProvider ecbRateProvider = MonetaryConversions
.getExchangeRateProvider("ECB");

CurrencyUnit real = MonetaryCurrencies.getCurrency("BRL");
CurrencyUnit dollar = MonetaryCurrencies.getCurrency(Locale.US);

CurrencyConversion ecbDollarConvertion = ecbRateProvider
.getCurrencyConversion(dollar);

CurrencyConversion imfDollarConvertion = imfRateProvider
.getCurrencyConversion(dollar);

MonetaryAmount money = Money.of(10, real);
System.out.println(money.with(ecbDollarConvertion));
System.out.println(money.with(imfDollarConvertion));
}

}

In this article was talked about about how take care of money in Java with JSR 354, this API is expected to Java 9.

Source: https://github.com/JavaMoney/javamoney-examples/tree/master/functional-example