Come on guys, this blog will not tell you how to create real money and I have no secret plates which will allow you to print real currency. This blog is about Enterprise Applications and how they deal with money.
We all work for money but no one really understands how money works. Enterprise Applications are mostly concerned with Money and Time. As developers, we always have to simulate this into our system. It is used so frequently in enterprise applications that it could be used as basic data types.
As an ignorant developer, I started developing code with integer. Then I realized that we can also have cents. Hence, I changed money to represent as a float.As far as addition or subtraction is concerned it worked well. The problem started when multiplication and division operations had to be performed on money. This is common during calculations of interest rates and dividend.
The problem here is that only central banks of a country have the right to create or destroy money. So your application is not supposed to create or lose money from your system. However, the computer calculation is not always accurate or precise. What seems obvious to you will not work with computer arithmetic. To elaborate this problem let’s take an example. Suppose you want to divide 12 Dollars into 70% and 30 % you would expect to as below:
12 * 0.3 = 03.60
12 * 0.7 = 08.40
Total = 12.00
Java treats 0.3 and 0.7 as double. However, due to the internal representation of the system and rounding the actual calculation happens as below:
12 * 0.3 = 03.59
12 * 0.7 = 08.39
Total = 11.98
This is incorrect as we are losing 2 cents in the whole calculation, which will scare Accountants.
Use BigDecimal
Now to resolve this problem we have to use BigDecimal. To make this example work we will be using the code as below:
BigDecimal dotThree = new BigDecimal(“0.3”);
dotThree.setScale(2, RoundingMode.HALF_DOWN);
BigDecimal dotSeven = new BigDecimal(“0.7”);
dotSeven.setScale(2, RoundingMode.HALF_DOWN);
BigDecimal result1 = twelve.multiply(dotThree);
BigDecimal result2 = twelve.multiply(dotSeven);
This will result in calculation which is expected as below:
12 * 0.3 = 3.6
12 * 0.7 = 8.4
Total = 12.0
To give fine grain control over BigDecimal we create wrapper class of BigDecimal which represents Money. Let’s call this class the same
class Money {
BigDecimal value;
public Money(String stValue)
{
value=new BigDecimal(stValue);
}
}
To avoid constantly setting the scale of currency class we can add one more constant called DEFAULT_SCALE. This can be different for different countries as well as different applications
class Money {
MathContext DEFAULT_SCALE=new MathContext(5,0);
BigDecimal value;
MathContext scale;
}
Describing Currency
Avoid using String to represent currency. This is error prone and will lead you to confusions like “USD, “usd” “Dollar”, which are same but will have different strings. We already have java.util.Currency class to do this.
Another interesting fact is that we can easily get Currency from Locale via
Currency currency = Currency.getInstance(Locale.US);
This is useful when you are developing web applications which pass your client browser location. We can also use ISO 4217 codes to create the currency object.
Currency currency = Currency.getInstance(“USD”);
Combined Result
class Money {
BigDecimal value;
Currency currency;
MathContext scale;
of(Currency curreny,Number number)
of(String currencyCode,Number number)
of(Currency currency,Number number,MathContext)
of(Currency currency,BigDecimal number)
}
We also have to copy all arithmetic operations which are available in BigDecimal class. They are mainly abs(), ulp(), pow(n), remainder(money),add(), doubleValue(), floadValue(), byteValue() but before applying any operation on this class we have to check the currencies are same.
public Money divide(Money divisor){
check(this.currency, divisor);
BigDecimal result = this.value.divide(value, this.scale);
return new Money(this.currency, result, this.scale);
}
private static final void check(Currency currency, Money amount){
if (amount == null) {
throw new IllegalArgumentException(
“Amount should not be null”);
}
final Currency amountCurrency = amount.currency;
if (!currency.getCurrencyCode().
equals(amountCurrency.getCurrencyCode())){
throw new IllegalArgumentException(
“Currency Mismatch happened”);
}
}
Limitations
JodaMoney
If your application heavily uses different currencies, you should look at JodaMoney. This is written by the author of JodaTime. Although not as popular as JodaTime, this is a viable option till JSR354 is part of JDK. It also provides good support for currency exchange.
JSR 354
This is planned to be part of JDK 9 which might be released in year 2016. There are new concepts added in this JSR like CurrencyUnit, MonetaryAmount and other classes.