Introduction to JSR-275: Measures and Units

New API specification removes uncertainty from programming with physical measurements

Page 2 of 4

Class parameterization

Class parameterization is arguably the single most important enhancement to the Java platform since its infancy. The parameterized collection framework (java.util.*) demonstrates only a small fragment of what can be done through parameterization. The JScience project has pushed the envelope even further by taking advantage of parameterization in new areas such as mathematical structures (Ring, Field, Group), vectors/matrices and functions.

Parameterization has many advantages, not least being a "zero-cost at run time" because all the checks are done at compile time. JSR 275 takes advantage of class parameterization to ensure dimension consistency. As Listing 5 shows, classes such as Measurable, Measure and Unit are parameterized by their quantity type (such as Mass or Length). The dimensions of a unit (or measure) can be retrieved from the unit itself (at run time) of inferred from its quantity type (at compile time).

 

Listing 5. Parameterization for dimension consistency

 Unit<length> inch = CENTI(METER).times(2.54); // OK.
Unit<length> inch = CENTI(LITER).times(2.54); // Compile error.

Measurable<Duration> d = Measure.valueOf(2, MINUTE); // OK.
Measurable<Duration> d = Measure.valueOf(2, WATT); // Compile Error.

long milliseconds = d.longValue(MILLI(SECOND)); // OK.
long milliseconds = d.longValue(MILLI(HERTZ)); // Compile error.

When the compiler can't ensure commensurability, it issues a warning that can easily be removed through an explicit run-time check of the dimensions or by using the ? wildcard parameterized type (see Listing 6).

Listing 6. Compiler warnings and how to remove them

 // Compiler Warnings
Unit<Acceleration> mps2 = Unit.valueOf("m/s²"); // Compiler Warning.
Unit<Power> WATT = JOULE.divide(SECOND); // Compiler Warning.

// Adding run-time check of dimension consistency removes the warning.
Unit<Acceleration> mps2 = Unit.valueOf("m/s²").asType(Acceleration.class); 
Unit<Power> WATT = JOULE.divide(SECOND).asType(Power.class);

// Use wildcard parameterized type (no warning and no check).
Unit<?> kelvinPerSec = KELVIN.divide(SECOND);

Units and units conversions

Units can be retrieved from constants held by various SystemOfUnits subclasses, such as SI (holding the "Système International d'Unités" standard units) or NonSI (holding nonstandard but common units). Custom systems of units can be created, and units can belong to several system of units. For example, the imperial system would contain many of the NonSI units. (You can use the SystemOfUnits.getUnits() member method to list all the units held by a system.)

Measurable or Measure?
Measurable is an interface allowing all kinds of implementations. It is basically equivalent to java.lang.Number and provides similarly named methods for conversion to primitive types (intValue(Unit), doubleValue(Unit)). Measure is the simple combination of a numerical value and a unit. Measurable is more flexible, but if you need to retrieve the original numeric value stated in its original unit (no conversion), then Measure is your only choice.

By using predefined units, users avoid many conversion pitfalls. For example, the dry gallon is different from the liquid gallon, which is different in the United States and in the United Kingdom. Differentiation can be done either through SystemOfUnits (an imperial system would contain the U.K. units exclusively) or through distinct names such as DAY_SIDEREAL and DAY_CALENDAR, which differentiate between the day corresponding to the rotation of the Earth (23 hours, 56 minutes, 4.09 seconds) and the 24-hour calendar day. All conversion factors have been established from the latest Bureau International des Poids et Mesures (BIPM) standard.

If a unit does not exist in the SI or NonSI system of units, you can create new units by applying algebraic operations on existing units. Table 1 shows the operations supported on unit instances.

 

Table 1. Units operations

Result with same dimensionResult with different dimension
Binary operations:Binary operations:
plus (double) or (long)root (int)
times (double) or (long)power (int)
divide (double) or (long)times (Unit)
compound (Unit)divide (Unit)
 Unary operations:
 inverse()
| 1 2 3 4 Page 2