Question

I use BigDecimal throughout my application to handle money and percentages. But, I need a way to differentiate usage between the two (for the purpose of rendering them, i.e., in a JTable). So, my initial thought was to write two classes which behave exactly like BigDecimal so that I could do instanceof tests on them. So I wrote this code:

DollarValue.java:

import java.math.BigDecimal;

public class DollarValue extends BigDecimal {

}

PercentValue.java:

import java.math.BigDecimal;

public class PercentValue extends BigDecimal {

}

However, in order to use the constructors (because they cannot be inherited), I you need to manually override every constructor. So, DollarValue.java became a monstrosity:

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;

public class DollarValue extends BigDecimal {

    public DollarValue(char[] in, int offset, int len) {
        super(in, offset, len);
    }
    public DollarValue(char[] in, int offset, int len, MathContext mc) {
        super(in, offset, len, mc);
    }
    public DollarValue(char[] in) {
        super(in);
    }
    public DollarValue(char[] in, MathContext mc) {
        super(in, mc);
    }
    public DollarValue(String val) {
        super(val);
    }
    public DollarValue(String val, MathContext mc) {
        super(val, mc);
    }
    public DollarValue(double val) {
        super(val);
    }
    public DollarValue(double val, MathContext mc) {
        super(val, mc);
    }
    public DollarValue(BigInteger val) {
        super(val);
    }
    public DollarValue(BigInteger val, MathContext mc) {
        super(val, mc);
    }
    public DollarValue(BigInteger unscaledVal, int scale) {
        super(unscaledVal, scale);
    }
    public DollarValue(BigInteger unscaledVal, int scale, MathContext mc) {
        super(unscaledVal, scale, mc);
    }
    public DollarValue(int val) {
        super(val);
    }
    public DollarValue(int val, MathContext mc) {
        super(val, mc);
    }
    public DollarValue(long val) {
        super(val);
    }
    public DollarValue(long val, MathContext mc) {
        super(val, mc);
    }
}

But what's worse, in implementation, this didn't work because I use the static BigDecimal code a lot. To illustrate:

public static void main(String[] args) {
    DollarValue dollars = DollarValue.ONE;
    DollarValue dollars2 = DollarValue.valueOf(25);
}

throws an error, because DollarValue.ONE is actually a BigDecimal, not a DollarValue and DollarValue#valueOf() returns a BigDecimal too.

So now I'm out of ideas. What is the easiest way to distinguish between two implementations of the same class in the way I'm trying to achieve?? This seems like a simple problem...

Edit: Any solution that allows me to automatically differentiate between the two in a renderer will also work. For instance, I need to feed the values to a JTable and have the JTable display either a dollar sign $ for money or a percent sign % for percentages.

Was it helpful?

Solution

I strongly recommend using the Joda-Money library for working with money, rather than trying to reinvent the wheel yourself. Working with money is a common, and tricky, problem. Let other people solve it for you.

It's always a little frustrating to add a new dependency to your project, but when it comes to core utility libraries that provide obvious, but tricky to implement, benefits, it should be a no-brainer. Some other critical libraries are:

  • Guava - this should absolutely be part of any modern Java codebase. The utilities and new collections provided are irreplaceable valuable.
  • Joda-Time (unless you're on Java 8) - like money, time is a deeply tricky data structure to properly represent (as Java's long-broken Calendar and associated classes prove) and Joda-Time provides a robust and well thought out interface.

There are many other libraries that I could call out as worth using, but you're truly making a mistake if you aren't using these three. While technically "non-standard", like you note, these are so ubiquitous that they aren't far from being conceptually expected parts of a Java developer's toolbelt.

OTHER TIPS

Use composition, rather than inheritance. Do all the arithmetic in BigDecimal, but have two classes that each has a constructor that takes a BigDecimal argument and a getValue method that returns the BigDecimal.

Code would look a bit more complicated:

DollarValue dollars = new DollarValue(BigDecimal.ONE);
DollarValue dollars2 = new DollarValue(BigDecimal.valueOf(25));
DollarValue sum = new DollarValue(dollars.getValue().add(dollars2.getValue()));

If there are a few really common operations for one of the classes, implement the directly in that class. For example, DollarValue could have:

public DollarValue sum(DollarValue other) {
  return new DollarValue(getValue().add(other.getValue()));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top