Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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"));