Superseded by Value Object Sample Code v2.Money and Currency are value types that you can implement as classes or enums. A simple straightforward implementation of Currency in Java 5 might utilize enums and look like this:
public enum Currency { USD("USD", '$', 2), EUR("EUR", '€', 2), JPY("JPY", '¥', 0); protected final String isoCode;
protected final char symbol;
protected final int noFractionDigits; private Currency(String myISOCode, char mySymbol, int myNoFractionDigits) {
isoCode = myISOCode;
symbol = mySymbol;
noFractionDigits = myNoFractionDigits;
}
…}Enums are a good tool to implement value types of known cardinality. If you know exactly what values there are and you can enumerate those, enums are your friends. Enums aren’t automatically made immutable, so you still need to implement them properly. Please note that we achieved immutability by making all fields final.
If you don’t know the exact number of your possible values, or if this number is simply too large to be enumerated easily, it is better to use a regular class. This is what the JDK does for currencies with the java.util.Currency class, which represents all 200+ ISO currencies there presently are.
package java.util;
…public final class Currency implements Serializable { private final String currencyCode;
transient private final int defaultFractionDigits;
…
private static HashMap instances = new HashMap(7);
…
private Currency(String currencyCode, int defaultFractionDigits) {
this.currencyCode = currencyCode;
this.defaultFractionDigits = defaultFractionDigits;
} public static Currency getInstance(String currencyCode) {
return getInstance(currencyCode, Integer.MIN_VALUE);
} private static Currency getInstance(String currencyCode, int defaultFractionDigits) {
synchronized (instances) {
…
} public String getCurrencyCode() {
return currencyCode;
}
…}A couple of things are interesting about java.util.Currency. First, it is a true Value Object class, that is, it is immutable and it its instances are used like values.
Secondly, it shares its instances: There can never be two Currency instances that both represent the US$. This shar-ing is realized by making the constructor private and forcing clients to use the static getInstance() method. A consequence of there never being duplicate Currency objects is that the Currency class does not have to implement equals() and hashcode(); the default implementations inherited from Object are sufficient.
Sharing is implemented in a straightforward way: Existing instances are maintained in a hash map. If an instance does not yet exist, it is created on the fly. This leads to an interesting trade-off that may or may not work for some clients. Within the getInstance() method, the code synchronizes on the instances hash map, incurring locking over-head. This is to avoid the problems with the double-checked locking pattern in Java
BBBetal00. The only way to avoid this performance penalty is to fully create all currencies upon system initialization, which has the downside of creating objects that most applications may never use.
Hidden in the Currency class code is the use of java.util.CurrencyData, which provides the initialization data for a new Currency value object. It is contains ISO 4217 currency code data. Unfortunately, there is no way of configuring this data so that your own reference data could be injected. For example, if you have your own symbols for precious metals, as many banks do, you may not be able to transparently use them as currencies. Also, java.util.Currency does not provide all information you may need. For example, minor units that belong to a currency (like ‘cent’ for the Dollar or Euro) are missing. So you may end up having to write your own currency class after all.