What is JSR 354?
JSR 354 is the upcoming standard, how money and currencies should be modelled and handled in Java. You can find more details here:
- Project Page on Java.net: http://java.net/projects/javamoney
- JCP main page: http://jcp.org/en/jsr/detail?id=354
- GitHub Repository: https://github.com/JavaMoney/javamoney
Why this blog?
Within JSR 354 we had a lot of discussions. I wanted to show with some examples how this API feels. And finally, if someone has input or ideas, we want to be aware of, so we finally build the right API !Dealing with Currencies
Basic Design Decisions
The JSR basically models many of the key artifacts using interfaces. Nevertheless we provide concrete value types implementing the interfaces which in concrete code are referenced. The interfaces typically are for interoperability:- CurrencyUnit and MonetaryAmout are modelled by interfaces for interoperability.
- The classes MoneyCurrency and Money implement these interfaces.
- In the case of MoneyCurrency, also an according MoneyCurrency.Builder is defined.
Interface javax.money.CurrencyUnit
Basically the interface directly models the same aspects as available on the existing java.util.Currency class, but adds some additional methods to support additional aspects not covered by java.util.Currency. Some of the considerations are:
- not renaming existing methods of java.util.Currency enables maximal backward compatibility and makes it more easy to let implement java.util.Currency the new interface for interoperability. This would also allow existing code to remain as is, if none of the extended features are required.
- the other methods added should be the minimum required and be basically easily implementable, If feasible, also undefined values should be possible as method results.
- In the area of currencies there are a couple of complexities that were not obvious at a first glance, but can not be neglected, if currency should model the reality at least to some extent. For example think on the following aspects:
- currencies come and go during human history. Even during the last years there were quite important changes that requires that historic and current currencies can be distinguished.
- ISO-4217 currency codes also have some imminent aspects to be considered:
- ISO codes (the identifiers!) are not guaranteed over time, the can be reused after some defined time.
- Rounding modes and fraction digits can change during time, even when the currency is still the same.
- Historic currencies are not mapped by ISO at all.
- ISO also maps things that are not effectively currencies, like precious metals (e.g. AUG), no currency (XXX) or testing codes.
- ISO also is ambiguous e.g. CFA is a code that basically is backed up by two different bank notes.
- the biggest change is that we introduced an additional namespace on top of the currency code, because:
- namespaces can be used to separate concerns. This makes sense since ISO currencies are real currencies, whereas Social or Video Game Currencies are completely virtual. BitCoin even is more special, since it started as a virtual currency but lately is accepted more and more as real currency.
- namespaces also allow to manage legacy currency schemes as they are in use by all financial organizations that must deal with money in a time range and scope that is longer than 10 years.
- One might argue, that we could simply extend the existing currency code. But this also has some severe drawbacks:
- the existing currency code of the JDK class can not be adapted correspondingly since this would break behavioral compatibility. But if the code can not be extended, we would have currencies with a namespace prefix (all non ISO), and ones without (ISO, as before).
- Additionally the JSR should definitely not impose anything on how currency codes will be defined in the future. But when extending the existing currency code with some optional namespace, an according separation criteria must be defined. Obviously this could easily clash with future namespace or code identifiers.
So summarizing a currency must implement the following interface:
public interface public CurrencyUnit{
public String getNamespace(); // new
public String getCurrencyCode();
public int getNumericCode();
public int getDefaultFractionDigits();
public boolean isVirtual(); // new
public boolean isLegalTender(); // new
public Long getValidFrom(); // new
public Long getValidUntil(); // new
}
- the numeric code, if not defined, should be -1.
- the timestamps are modelled as Long due to the following reasons:
- the JSR wants to be backward compatible with SE 7 (which will also cover SE6).
- SE 8 is not yet final.
- Long can be null, which means not defined. This is also the correct value for current currency instances.
- UTC timestamps are commonly understood and well supported by all kind of time and date frameworks. They can easily converted to any other kind of objects required. Note: there is quite discussion ongoing, if the new JSR 310 APIs should be used here. We require definitively here better information, if we can go for the new date and time types, without preventing usage of the new API for years.
Class javax.money.MoneyCurrency
This class is the implementation of CurrencyUnit, also including an according Builder and an internal cache for reusing instances created. It is also where, by default, in the new API ISO currencies can be accessed:
MoneyCurrency currency = MoneyCurrency.of("USD");
For ISO currencies this will work out of the box, since the ISO currencies are implicitly backed up by java.util.Currency. Also the call above does implicitly add 'ISO-4217' as namespace, so basically the call below is aequivalent:
MoneyCurrency currency = MoneyCurrency.of("ISO-4217", "USD");
Defining alternate MoneyCurrency instances and namespaces can be done using the MoneyCurrency.Builder:
MoneyCurrency.Builder builder = new MoneyCurrency.Builder();
builder.setNamespace("myNamespace");
builder.setCurrencyCode("myCode");
builder.setDefaultFractionDigits(4);
builder.setLegalTender(false);
builder.setVirtual(true);
builder.setAttribute("test-only", true);
MoneyCurrency unit = builder.build();
// however MoneyCurrency.of("myNamespace", "myCode");
// still returns null!
builder.build(true);
// no it is registered
unit = MoneyCurrency.of("myNamespace", "myCode");
Extended API: javax.money.ext.MonetaryCurrencies
This singleton class allow to access currencies in a more service like fashion as required for more advanced use cases, such as accessing historic currencies or mapping of currencies between or within namespaces. Also it is possible to access all currencies within a namespace. Extended functionality is not part of the JSR's platform part and therefore is designed as standalone module, usable in multiple usage contexts (SE, EE etc):
// historic access
public static boolean isNamespaceDefined(String namespace,
Long timestamp){..}
public static Collection<String> getNamespaces(
Long timestamp){..}
public static CurrencyUnit get(String namespace, String code,
Long timestamp){..}
public static CurrencyUnit get(String code, Long timestamp){..}
public static Collection<CurrencyUnit> getAll(String namespace,
Long timestamp){..}
public static boolean isDefined(String namespace, String code,
Long timestamp){..}
public static boolean isDefined(String code,
Long timestamp){..}
public static Collection<CurrencyUnit> getAll(Locale locale,
Long timestamp){..}
// mapping of currrencies
The singleton's implementation itself can be determined by registering an instance of MonetaryCurrencies.MonetaryCurrenciesSpi using java.util.ServiceLoader.
public interface MonetaryAmount{
CurrencyUnit getCurrency()
MonetaryAmount add(MonetaryAmount);
MonetaryAmount subtract(MonetaryAmount);
MonetaryAmount multiply(MonetaryAmount);
MonetaryAmount multiply(Number);
public final class MonetaryCurrencies {
public static boolean isNamespaceDefined(String namespace){..}
public static Collection<String> getNamespaces(){..}
public static CurrencyUnit get(String namespace,
String code){..}
String code){..}
public static Collection<CurrencyUnit> getAll(
String namespace){..}
String namespace){..}
public static boolean isDefined(String code){..}
public static boolean isDefined(String namespace,
String code){..}
String code){..}
public static Collection<CurrencyUnit> getAll(String namespace,
String code){..}
String code){..}
public static Collection<CurrencyUnit> getAll(String code){..}
// historic access
Long timestamp){..}
public static Collection<String> getNamespaces(
Long timestamp){..}
public static CurrencyUnit get(String namespace, String code,
Long timestamp){..}
public static CurrencyUnit get(String code, Long timestamp){..}
public static Collection<CurrencyUnit> getAll(String namespace,
Long timestamp){..}
public static boolean isDefined(String namespace, String code,
Long timestamp){..}
public static boolean isDefined(String code,
Long timestamp){..}
public static Collection<CurrencyUnit> getAll(Locale locale,
Long timestamp){..}
// mapping of currrencies
public static CurrencyUnit map(CurrencyUnit unit,
String targetNamespace);
String targetNamespace);
public static List<CurrencyUnit> mapAll(String targetNamespace,
CurrencyUnit... units);
CurrencyUnit... units);
public static CurrencyUnit map(CurrencyUnit unit,
String targetNamespace,
Long timestamp);
String targetNamespace,
Long timestamp);
public static List CurrencyUnit> mapAll(String targetNamespace,
Long timestamp,
CurrencyUnit... units);
}Long timestamp,
CurrencyUnit... units);
Monetary Amounts
Some Basic Design Decisions
Monetary amounts basically follow the same design as CurrencyUnit::
- MonetaryAmout is modelled by interface for interoperability.
- Aims look and feel from java.math.BigDecimal
- Money implements the MonetaryAmout interface, using java.math.BigDecimal for numeric representation.
Interface javax.money.MonetaryAmount
Basically the interface models similar aspects as java.math.BigDecimal, but targeting monetary amounts:
public interface MonetaryAmount{
CurrencyUnit getCurrency()
MonetaryAmount add(MonetaryAmount);
MonetaryAmount subtract(MonetaryAmount);
MonetaryAmount multiply(MonetaryAmount);
MonetaryAmount multiply(Number);
MonetaryAmount divide(MonetaryAmount);
MonetaryAmount divide(Number);
MonetaryAmount remainder(MonetaryAmount);
MonetaryAmount remainder(MonetaryAmount);
MonetaryAmount remainder(Number);
MonetaryAmount scaleByPowerOfTen(int);
MonetaryAmount abs();
MonetaryAmount negate();
MonetaryAmount pow(int);
MonetaryAmount scaleByPowerOfTen(int);
MonetaryAmount abs();
MonetaryAmount negate();
MonetaryAmount pow(int);
MonetaryAmount ulp(); // unit in the last place
boolean isGreater(MonetaryAmount);
boolean isGreaterOrEquals(MonetaryAmount);
boolean isLess(MonetaryAmount),
boolean isLessOrEquals(MonetaryAmount);
...
boolean isEqualTo(MonetaryAmount);
boolean isNotEqualTo(MonetaryAmount);
boolean isNegative();
boolean isPositive();
boolean isZero();
byte byteValue();
short shortValue();
short shortValueExact();
int intValue();
int intValueExact();
boolean isGreater(MonetaryAmount);
boolean isGreaterOrEquals(MonetaryAmount);
boolean isLess(MonetaryAmount),
boolean isLessOrEquals(MonetaryAmount);
...
boolean isEqualTo(MonetaryAmount);
boolean isNotEqualTo(MonetaryAmount);
boolean isNegative();
boolean isPositive();
boolean isZero();
byte byteValue();
short shortValue();
short shortValueExact();
int intValue();
int intValueExact();
long longValue();
long longValueExact();
float floatValue();
double doubleValue();
<T> T asType(Class<T>);
Class<?> getNumberType();
int getPrecision();
int getScale();
long longValueExact();
float floatValue();
double doubleValue();
<T> T asType(Class<T>);
Class<?> getNumberType();
int getPrecision();
int getScale();
int signum();
MonetaryAmount from(Number);
MonetaryAmount from(CurrencyUnit, Number);
MonetaryAmount with(MonetaryOperator);
}
MonetaryAmount from(Number);
MonetaryAmount from(CurrencyUnit, Number);
MonetaryAmount with(MonetaryOperator);
}
Hereby the last method with, allows to combine amount instances with arbitrary external manipulation logic, implemented as MonetaryOperator. This is explained in more detail later.
Hereby the above calls also include some convenience, since the ISO namespace is used implicitly. The first call basically is equivalent to:
Money amount1 = Money.of(MoneyCurrency.of("USD"), 12);
or to the full fledged version:
Money amount1 =
Money.of(
MoneyCurrency.of(MoneyCurrency.ISO_NAMESPACE, "USD"), 12);
Class javax.money.Money
This class is the implementation of MonetaryAmount, using java.math.BigDecimal for numeric representation. The method signature look similar to the interface, but return the according value object, e.g.:
public final class Money implements MonetaryAmount,...{
[...]
public Money add(MonetaryAmount amount);
[...]
}
public final class Money implements MonetaryAmount,...{
[...]
public Money add(MonetaryAmount amount);
[...]
}
Creation of Money instance hereby is done using the of() factory methods, e.g.
Money amount1 = Money.of("USD", 12); // int
Money amount2 = Money.of("USD", 12.5); // float
Money amount3 = Money.of("USD", (byte)12); // int
Money amount4 = Money.of("USD", BigDecimal.valueOf(100.15d));
// BigDecimal
Money amount2 = Money.of("USD", 12.5); // float
Money amount3 = Money.of("USD", (byte)12); // int
Money amount4 = Money.of("USD", BigDecimal.valueOf(100.15d));
// BigDecimal
Hereby the above calls also include some convenience, since the ISO namespace is used implicitly. The first call basically is equivalent to:
Money amount1 = Money.of(MoneyCurrency.of("USD"), 12);
or to the full fledged version:
Money amount1 =
Money.of(
MoneyCurrency.of(MoneyCurrency.ISO_NAMESPACE, "USD"), 12);
Interface javax.money.MonetaryOperator
As seen before an instance of MonetaryAmount also has a with method taking a MonetaryOperator as a parameter. The type MonetaryOperator is modelled similarly to the UnaryOperator functional interface from Java 8, extending the corresponding base type MonetaryFunction:
//@FunctionalInterface
public interface MonetaryFunction<T, R> {
public R apply(T value);
}
//@FunctionalInterface
public interface MonetaryOperator
extends MonetaryFunction<MonetaryAmount,MonetaryAmount> {
}
//@FunctionalInterface
public interface MonetaryFunction<T, R> {
public R apply(T value);
}
//@FunctionalInterface
public interface MonetaryOperator
extends MonetaryFunction<MonetaryAmount,MonetaryAmount> {
}
This looks not very spectacular, but when combined with functionalities like currency exchange, rounding and more complex operations the concepts renders to a powerful weapon against complexity:
CurrencyConversion convertToYen = ...;
Money m = Money.of("USD", 12345.25)
.multiply(10.34563)
.with(MajorPart.of())
.with(convertToYen)
.divide(7)
.with(MoneyRounding.of());
This operational chain creates an amount in USD, multiplies it by 10.34563, takes the major part only, converts it to YEN, divides by 7 and finally rounds according the default rounding rules defined for YEN.
When dealing with monetary functions there are also some built-in functions provided by default (if you have some hints or ideas, what else would be useful please drop me a mail):
Collection<MonetaryAmount> amounts = ...;
Map<CurrencyUnit, MonetaryAmount> sepResult =
SeparateCurrencies.apply(amounts);
// Get amounts > 100 in USD
Collection<MonetaryAmount> allUSDAmounts =
sepResult.get(MoneyCurrency.of("USD"));
Collection<MonetaryAmount> bigAmounts = new
AmountFilter(allUSDAmounts ).apply(
new MonetaryFunction<MonetaryAmount, Boolean>(){
public Boolean apply(MonetaryAmount amount){
return amount.intValue()>100;
}
});
Finally also rounding can be modelled similar as MonetaryOperator, thus being only a special case of operation not adding basic additional complexity on this API level. Hereby the class MoneyRounding provides access to rounding algorithms:
public final class MoneyRounding implements MonetaryOperator{
public MonetaryAmount apply(MonetaryAmount amount){
[.. ]
}
}
As an example, refer to the example below:
Money mm = Money.of("USD", "1222222222222222222323232.23232323");
IntegralMoney im = IntegralMoney.of("USD", 2345);
mm.divide(im); // should work
im.divide(mm); // throws ArithmeticException
So hopefully, this creates some appetite for more. So go to our project page on java.net or check out our GitHub repository and try things out, and of course, if you have any questions, or even better feedback or improvement, feel free to contact me.
CurrencyConversion convertToYen = ...;
Money m = Money.of("USD", 12345.25)
.multiply(10.34563)
.with(MajorPart.of())
.with(convertToYen)
.divide(7)
.with(MoneyRounding.of());
This operational chain creates an amount in USD, multiplies it by 10.34563, takes the major part only, converts it to YEN, divides by 7 and finally rounds according the default rounding rules defined for YEN.
When dealing with monetary functions there are also some built-in functions provided by default (if you have some hints or ideas, what else would be useful please drop me a mail):
Collection<MonetaryAmount> amounts = ...;
Map<CurrencyUnit, MonetaryAmount> sepResult =
SeparateCurrencies.apply(amounts);
// Get amounts > 100 in USD
Collection<MonetaryAmount> allUSDAmounts =
sepResult.get(MoneyCurrency.of("USD"));
Collection<MonetaryAmount> bigAmounts = new
AmountFilter(allUSDAmounts ).apply(
new MonetaryFunction<MonetaryAmount, Boolean>(){
public Boolean apply(MonetaryAmount amount){
return amount.intValue()>100;
}
});
Finally also rounding can be modelled similar as MonetaryOperator, thus being only a special case of operation not adding basic additional complexity on this API level. Hereby the class MoneyRounding provides access to rounding algorithms:
public final class MoneyRounding implements MonetaryOperator{
public MonetaryAmount apply(MonetaryAmount amount){
[.. ]
}
}
Different Numeric Representations
Different implementations of MonetaryAmount can model the numeric representation using different numeric types. This allows to cover the varieties of use cases identified. Nevertheless it should be possible to mix up different implementations by applying some relatively simple rules:
- The numeric representation is not part of the MonetaryAmount interface.
- The target instance's class of an operation performed, determines the resulting instance type of an operation (which should be the same as the target type itself).
- If an arithmetic operation exceeds the capabilities of the target instance, an ArithmeticException should be thrown.
- Precision/Scale information should never be lost.
- Automatic rounding is only valid, when required by the internal representation. By default no rounding should ever happen, despite internal rounding, implied by the numeric representation type.
As an example, refer to the example below:
Money mm = Money.of("USD", "1222222222222222222323232.23232323");
IntegralMoney im = IntegralMoney.of("USD", 2345);
mm.divide(im); // should work
im.divide(mm); // throws ArithmeticException
So hopefully, this creates some appetite for more. So go to our project page on java.net or check out our GitHub repository and try things out, and of course, if you have any questions, or even better feedback or improvement, feel free to contact me.