i have different classes representing differents units (volume, weight, distance), with an enum with a value for storing the conversion values to a base type.
The problem is that there is a lot of code duplication and
i’m sure there is an elegant way to write an abstract class that would avoid it.
I don’t know how i should declare this class though.
Here is the volume class:
import java.math.BigDecimal;
import lombok.Data;
@Data
public class Volume {
public enum Unit
{
CUBIC_METER(new BigDecimal("1")), // CUBIC_METER
// 1 m3 = 61023.7 inch3
CUBIC_INCH(new BigDecimal("61023.7"));
private Unit(BigDecimal m3Value)
{
this.m3Value = m3Value;
}
public final BigDecimal m3Value;
}
// internally, we store the volume in m3
private final BigDecimal volumeInM3;
public Volume()
{
volumeInM3 = BigDecimal.ZERO;
}
public Volume(final String volumeValue, final Unit volumeUnit)
{
this(new BigDecimal(volumeValue), volumeUnit);
}
public Volume(final BigDecimal volumeValue, final Unit volumeUnit)
{
if (volumeValue.signum() == 0)
{
volumeInM3 = BigDecimal.ZERO;
}
else
{
volumeInM3 = volumeValue.divide(volumeUnit.m3Value, NumberUtil.MC);
}
}
/**
* Return the volume in the unit given in param
* @param volumeUnit
* @return
*/
public BigDecimal getAs(final Unit volumeUnit)
{
return volumeInM3.multiply(volumeUnit.m3Value, NumberUtil.MC);
}
public Volume add(final Volume volumeToAdd)
{
BigDecimal newVolumeValue = volumeToAdd.volumeInM3.add(volumeInM3, NumberUtil.MC);
return new Volume(newVolumeValue, Volume.Unit.CUBIC_METER);
}
public Volume divide(final Volume divisor)
{
BigDecimal newVolumeValue = volumeInM3.divide(divisor.volumeInM3, NumberUtil.MC);
return new Volume(newVolumeValue, Volume.Unit.CUBIC_METER);
}
public boolean isZero()
{
if (volumeInM3.signum() == 0)
return true;
return false;
}
public boolean isEqual(final Volume another)
{
if (volumeInM3.compareTo(another.volumeInM3) == 0)
return true;
return false;
}
}
And here is the quite similar Weight class:
import java.math.BigDecimal;
import lombok.Data;
@Data
public class Weight {
// the value stored with enum is the value used to convert the unit to kilogramm,
// wich is the reference unit
public enum Unit
{
KILOGRAM(new BigDecimal("1")),
// 1 kg = 1000 g
GRAM(new BigDecimal("1000")),
// 1 kg = 2.20462 pounds
POUND(new BigDecimal("2.20462"));
private Unit(BigDecimal kgValue)
{
this.kgValue = kgValue;
}
private final BigDecimal kgValue;
}
// internally, we store the weight inKg
private final BigDecimal weightInKg;
public Weight()
{
weightInKg = BigDecimal.ZERO;
}
public Weight(final String weightValue, final Unit weightUnit)
{
this(new BigDecimal(weightValue), weightUnit);
}
public Weight(final BigDecimal weightValue, final Unit weightUnit)
{
if (weightValue.signum() == 0)
{
weightInKg = BigDecimal.ZERO;
}
else
{
weightInKg = weightValue.divide(weightUnit.kgValue, NumberUtil.MC);
}
}
/**
* Return the weight in the unit given in param
* @param weightUnit
* @return
*/
public BigDecimal getAs(final Unit weightUnit)
{
return weightInKg.multiply(weightUnit.kgValue, NumberUtil.MC);
}
public Weight add(final Weight weightToAdd)
{
BigDecimal newWeightValue = weightToAdd.weightInKg.add(weightInKg, NumberUtil.MC);
return new Weight(newWeightValue, Weight.Unit.KILOGRAM);
}
public Weight divide(final Weight divisor)
{
BigDecimal newWeightValue = weightInKg.divide(divisor.weightInKg, NumberUtil.MC);
return new Weight(newWeightValue, Weight.Unit.KILOGRAM);
}
public boolean isZero()
{
if (weightInKg.signum() == 0)
return true;
return false;
}
public boolean isEqual(final Weight another)
{
if (weightInKg.compareTo(another.weightInKg) == 0)
return true;
return false;
}
}
When instanciating a volume, the user is forced to provide explicitely the unit.
Volume myCubicMeter, myCubicInch;
myCubicMeter = new Volume("1", Volume.Unit.CUBIC_METER);
myCubicInch = new Volume("1", Volume.Unit.CUBIC_INCH);
What i’m trying to achieve is an abstract class that would implement all the methods and force the subclasses to implements the enumeration with values.
What would be the correct way to do that?
In Java, enumerations are classes that are subclasses of the provided
Enumclass.You cannot give them a common abstract ancestor, neither you can subclass them. If you really want to keep the commodity of referring to your units with compile time constants from a set of an enumeration then you can’t do it.
If you discard using enumerations and maybe declare them as normal classes then you can do it freely as with everything else, eg