Select one of the blocks as the base block (one for weight, for example, gr , and another for distance, for example, m ). Then add the toBaseUnit and fromBaseUnit to your listing using conversion rates for each of your values.
There are no "ifs", and your conversion methods will look like this:
ResultingUnit.fromBaseUnit(originalUnit.toBaseUnit(value));
Listing example:
public enum Distance { METER(new BigDecimal("1.0")), // Base Unit is METER KM(new BigDecimal("1E3")), CM(new BigDecimal("1E-2")); private final static MathContext MC = new MathContext(30, RoundingMode.HALF_EVEN); private final BigDecimal conversionRatio; Distance(BigDecimal conversionRatio) { this.conversionRatio = conversionRatio; } long fromBaseUnit(BigDecimal baseUnit) { return baseUnit.divide(conversionRatio, MC).longValue(); } // returns BigDecimal to avoid rounding two times // and possible division by zero BigDecimal toBaseUnit(long originalUnit) { return BigDecimal.valueOf(originalUnit).multiply(conversionRatio); } }
And an adapted conversion method:
public long metricConvLength(long value, Distance orgUnit, Distance resultUnit) { return resultUnit.fromBaseUnit(orgUnit.toBaseUnit(value)); }
Also, to be a little faster (avoiding Map searching and looping enumeration values), the main advantage of this approach is that if you ever need a more complicated conversion operation (e.g. Temperature enumeration with Celsius, Farenheit and Kelvin) you can override fromBaseUnit and toBaseUnit in the body of the enumeration value.
Working example .
I used BigDecimal for conversion factor and internal calculations to have more control over precision and rounding.
source share