...
Here is an example base class for a numeric microtype. It exposes basic numeric operations, and also allows for derived classes to specify the maximum amount of accuracy that the type will allow.
| Code Block |
|---|
public abstract class DecimalValue<T extends DecimalValue> implements Comparable<T> { // Protected so that different subclasses of DecimalValue can perform maths upon each other protected final BigDecimal value; protected DecimalValue(BigDecimal value) { try { this.value = value.setScale(scale(), rounding()); } catch (ArithmeticException e) { throw new IllegalArgumentException(value + " is incompatible with rounding mode of " + rounding() + " given scale of " + scale(), e); } } protected abstract int scale(); protected abstract RoundingMode rounding(); @SuppressWarnings({"unchecked"}) @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DecimalValue<T> that = (DecimalValue<T>) o; return this.value.equals(that.value); } @Override public String toString() { return value.toString(); } public int compareTo(T that) { return this.value.compareTo(that.value); } public boolean isZero() { return value.compareTo(BigDecimal.ZERO) == 0; } public boolean isGreaterThan(T that) { return compareTo(that) == 1; } public boolean isGreaterThanOrEqualTo(T that) { return compareTo(that) >= 0; } public boolean isLessThan(T that) { return compareTo(that) == -1; } public boolean isLessThanOrEqualTo(T that) { return compareTo(that) <= 0; } public BigDecimal minus(final T that) { return this.value.subtract(that.value); } @Override public int hashCode() { return value.hashCode(); } public static <T extends DecimalValue<T>> BigDecimal exposeRawValueForMarshallingOrFormattingOnlyNotForBusinessLogic(T thing) { return thing.value; } public static <T extends DecimalValue<T>> double exposeDoubleValue(T thing) { return thing.value.doubleValue(); } // Protected so that subclasses can perform calculations upon one another protected static <T extends DecimalValue<T>> BigDecimal asBigDecimal(T decimalValue) { return decimalValue.value; } public String format(String pattern) { return format(pattern, value); } public static String format(String pattern, Number number) { return new DecimalFormat(pattern).format(number); } } |
...
Here is an example of a concrete numeric type.
| Code Block |
|---|
public class Delta extends DecimalValue<Delta> {
private Delta(BigDecimal value) {
super(value);
}
public static Delta delta(String value) {
return new Delta(new BigDecimal(value));
}
public static Delta delta(Double value) {
return new Delta(new BigDecimal(value));
}
protected int scale() {
return 8;
}
protected RoundingMode rounding() {
return HALF_UP;
}
}
|
A base string class
| Code Block |
|---|
public abstract class DelegateStringId<T extends DelegateStringId<T>> implements Comparable<T> {
public final String value;
protected DelegateStringId(String value) {
this.value = value;
if(value == null) {
throw new IllegalArgumentException("Cannot have a null " + getClass().getSimpleName());
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DelegateStringId that = (DelegateStringId) o;
return value.equals(that.value);
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public String toString() {
return value;
}
public int compareTo(T other) {
if ( this.getClass().equals(other.getClass())) {
return this.value.compareTo(((DelegateStringId) other).value);
}
throw new ClassCastException("Can only compare exactly same types");
}
}
|
...
Note that we use a static method, rather than a constructor to create the object, it has exactly the same effect, but is just that little bit more readable.
| Code Block |
|---|
public class Isin extends DelegateStringId<Isin> {
public Isin(final String value) {
super(value);
}
public static Isin of(String isin) {
return new Isin(isin);
}
}
|
It makes the code read much more like english when you have
| Code Block |
|---|
doSomeThing( Isin.of("DE00101110110"), Ric.of("VOD.L"));
|
rather than
| Code Block |
|---|
doSomeThing( new Isin("DE00101110110"), new Ric("VOD.L"));
|