[wpiunits] Change units to track their base unit, instead of their base class (#6342)

Unit objects now have a reference to the base unit from which they're derived. Constructing a unit object without specifying a base unit implicitly signifies that it's its own base unit, eg new Angle(null, 1, "Radian", "rad") would be the base angle unit of radians, while new Angle(Radians, 2 * PI, "Rotation", "R") would be a new angle unit based on radians.

This fixes much of the hacky code surrounding the derived unit types Velocity, Per, and Mult, but is a breaking change for any user code that defines custom unit classes or uses the anonymous unit type.
This commit is contained in:
Sam Carlberg
2024-05-12 09:15:56 -04:00
committed by GitHub
parent dc00a13d83
commit 6c9dcc157e
23 changed files with 312 additions and 162 deletions

View File

@@ -18,7 +18,7 @@ public class Unit<U extends Unit<U>> {
private final UnaryFunction m_toBaseConverter;
private final UnaryFunction m_fromBaseConverter;
final Class<? extends U> m_baseType; // package-private for the builder
private final U m_baseUnit;
private Measure<U> m_zero;
private Measure<U> m_one;
@@ -29,19 +29,21 @@ public class Unit<U extends Unit<U>> {
/**
* Creates a new unit defined by its relationship to some base unit.
*
* @param baseType the base type of the unit, e.g. Distance.class for the distance unit
* @param baseUnit the base unit, e.g. Meters for distances. Set this to {@code null} if the unit
* being constructed is its own base unit
* @param toBaseConverter a function for converting units of this type to the base unit
* @param fromBaseConverter a function for converting units of the base unit to this one
* @param name the name of the unit. This should be a singular noun (so "Meter", not "Meters")
* @param symbol the short symbol for the unit, such as "m" for meters or "lb." for pounds
*/
@SuppressWarnings("unchecked")
protected Unit(
Class<? extends U> baseType,
U baseUnit,
UnaryFunction toBaseConverter,
UnaryFunction fromBaseConverter,
String name,
String symbol) {
m_baseType = Objects.requireNonNull(baseType);
m_baseUnit = baseUnit == null ? (U) this : baseUnit;
m_toBaseConverter = Objects.requireNonNull(toBaseConverter);
m_fromBaseConverter = Objects.requireNonNull(fromBaseConverter);
m_name = Objects.requireNonNull(name);
@@ -51,16 +53,42 @@ public class Unit<U extends Unit<U>> {
/**
* Creates a new unit with the given name and multiplier to the base unit.
*
* @param baseType the base type of the unit, e.g. Distance.class for the distance unit
* @param baseUnit the base unit, e.g. Meters for distances
* @param baseUnitEquivalent the multiplier to convert this unit to the base unit of this type.
* For example, meters has a multiplier of 1, mm has a multiplier of 1e3, and km has
* multiplier of 1e-3.
* @param name the name of the unit. This should be a singular noun (so "Meter", not "Meters")
* @param symbol the short symbol for the unit, such as "m" for meters or "lb." for pounds
*/
protected Unit(
Class<? extends U> baseType, double baseUnitEquivalent, String name, String symbol) {
this(baseType, x -> x * baseUnitEquivalent, x -> x / baseUnitEquivalent, name, symbol);
protected Unit(U baseUnit, double baseUnitEquivalent, String name, String symbol) {
this(baseUnit, x -> x * baseUnitEquivalent, x -> x / baseUnitEquivalent, name, symbol);
}
/**
* Gets the base unit of measurement that this unit is derived from. If the unit is the base unit,
* the unit will be returned.
*
* <pre><code>
* Unit baseUnit = new Unit(null, ...);
* baseUnit.getBaseUnit(); // returns baseUnit
*
* Unit derivedUnit = new Unit(baseUnit, ...);
* derivedUnit.getBaseUnit(); // returns baseUnit
* </code></pre>
*
* @return the base unit
*/
public U getBaseUnit() {
return m_baseUnit;
}
/**
* Checks if this unit is the base unit for its own system of measurement.
*
* @return true if this is the base unit, false if not
*/
public boolean isBaseUnit() {
return this.equals(m_baseUnit);
}
/**
@@ -240,7 +268,7 @@ public class Unit<U extends Unit<U>> {
* @return true if both units are equivalent, false if not
*/
public boolean equivalent(Unit<?> other) {
if (this.m_baseType != other.m_baseType) {
if (!getClass().equals(other.getClass())) {
// different unit types, not compatible
return false;
}
@@ -265,15 +293,12 @@ public class Unit<U extends Unit<U>> {
return false;
}
Unit<?> that = (Unit<?>) o;
return m_baseType.equals(that.m_baseType)
&& m_name.equals(that.m_name)
&& m_symbol.equals(that.m_symbol)
&& this.equivalent(that);
return m_name.equals(that.m_name) && m_symbol.equals(that.m_symbol) && this.equivalent(that);
}
@Override
public int hashCode() {
return Objects.hash(m_toBaseConverter, m_fromBaseConverter, m_baseType, m_name, m_symbol);
return Objects.hash(m_toBaseConverter, m_fromBaseConverter, m_name, m_symbol);
}
/**