Measure.java
package de.turnertech.measures;
import java.util.Objects;
/**
* <p>A Measure is a description of some quantity of a {@link Unit}, where a {@link Unit} is a standardised and
* accepted quantity, size, ammount, period or other kind of measurement. For example, a {@link Unit#METRE}
* represents a standardised length.</p>
*
* <p>This implementation stores an immutable {@link Unit} instance. To obtain a {@link Measure} with another
* {@link Unit} you must either explicitely instantiate a new instance, or convert the Measure using one of the
* either {@link #convertTo(Unit)} or the class {@link UnitConverter}</p>
*/
public class Measure {
private double quantity;
private final Unit unit;
/**
* Constructs an instance with the provided parameters.
* @param quantity The quantity of Units in this Measure. Mutable.
* @param unit The Unit of this Measure. This is immutable.
*/
public Measure(final double quantity, final Unit unit) {
this.quantity = quantity;
this.unit = Objects.requireNonNull(unit, "Unit instance provided to Measure constructor may not be null");
}
/**
* <p>Helper function for checking if the Measure is equal to within a supplied
* tolerance. This is usefull in several situations as the Double Precision
* mechanism can very quickly introduce innacuracies which prevent .equals()
* from providing a useful result.</p>
*
* <p>If the Measures do not suare the same base unit, a conversion will
* first be attempted.</p>
*
* @param other Measure to check against.
* @param tolerance allowed difference between the numbers.
* @return true or false.
*/
public boolean equalsWithTolerance(final Measure other, final double tolerance) {
final Measure otherInThisUnit = UnitConverter.convert(other.quantity, other.unit, unit);
final double difference = Math.abs(this.quantity - otherInThisUnit.quantity);
return difference <= tolerance;
}
/**
* Gets the quantity of Units stored in this Measure.
* @return the quantity of Units stored in this Measure.
*/
public double getQuantity() {
return quantity;
}
/**
* Sets the quantity of Units stored in this Measure.
* @param quantity the quantity of Units stored in this Measure.
*/
public void setQuantity(final double quantity) {
this.quantity = quantity;
}
/**
* Sets the Unit of this Measure.
* @return the Unit of this Measure.
*/
public Unit getUnit() {
return unit;
}
/**
* <p>Helper function to convert this Measure to a new Measure with the
* supplied newUnit parameter. This function exists in place of a setter as
* the unit member is immutable.</p>
*
* <p>Internally this function simply delegates to
* {@link UnitConverter#convert(Measure, Unit)}</p>
*
* @param newUnit to be used in the creation of the new Measure instance.
* @return a new Measure instance in the supplied Unit.
*/
public Measure convertTo(final Unit newUnit) {
return UnitConverter.convert(this, newUnit);
}
@Override
public int hashCode() {
int hash = 5;
hash = 61 * hash + (int) (Double.doubleToLongBits(this.quantity) ^ (Double.doubleToLongBits(this.quantity) >>> 32));
hash = 61 * hash + Objects.hashCode(this.unit);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Measure other = (Measure) obj;
if (Double.doubleToLongBits(this.quantity) != Double.doubleToLongBits(other.quantity)) {
return false;
}
return Objects.equals(this.unit, other.unit);
}
/**
* We use the SI standard for the String representation. There is always a
* space between the quantity and unit, unless it is a planar angle.
* @return the Measure as a string formatted as per the SI Brochure section
* 5.3.3
*/
@Override
public String toString() {
if(unit == Unit.DEGREE || unit == Unit.MINUTE_ANGLE || unit == Unit.SECOND_ANGLE) {
return String.format("%f%s", quantity, unit.toString());
}
return String.format("%f %s", quantity, unit.toString());
}
}