Introduction to JSR-275: Measures and Units

New API specification removes uncertainty from programming with physical measurements

Page 4 of 4

As a minimum, the unit format recognizes the 20 SI prefixes used to form decimal multiples and submultiples of SI units, as shown in Listing 11.

Listing 11. Using SI prefixes

 Unit.valueOf("m°C").equals(SI.MILLI(SI.CELSIUS)) // True
Unit.valueOf("kW").equals(SI.KILO(SI.WATT)) // True
Unit.valueOf("ft").equals(SI.METER.multiply(3048).divide(10000)) // True

Unit.valueOf("ft").equals(SI.METER.multiply(0.3048)) // True!

In Listing 11, the last assertion is true, which might be a little puzzling, given that the double 0.3048 is about 0.3048000000000000154 (when represented using 19 digits), and the foot international unit (ft) has been standardized to be exactly 3048/10000 meters. The assertion is true because two converters are considered equals if their quotient is the identity converter. The concatenation of a rational converter and a multiply converter (or its inverse) is a multiply converter (inexact) for which equality comparison is not strict to account for numeric errors.

The unit format recognizes product units. The following strings, for example, all represent the same meter per square second units: "m*s-2", "m/s²", "m·s-²", and "m*s**-2" .

As I mentioned earlier, compound units can be used to format Measure in a canonical (unique) form using integer-value coefficients, as shown in Listing 12.

Listing 12. Compound units

 Unit<Duration> HOUR_MINUTE_SECOND = HOUR.compound(MINUTE).compound(SECOND);

Measure<Integer, Duration> d = Measure.valueOf(12345, SECOND);
System.out.println(d.to(HOUR_MINUTE_SECOND));

>> 3h25min45s

Dimensional analysis

A question often asked is why Unit, Measure and Measurable are parameterized by the quantity type and not some dimension type.

The first easy answer is that there is no such thing as a "dimension type." You'll find dimension instances that can be operated upon (product/quotient of dimensions), but class parameterization works only with classes, not instances. Also, using a dimension class for parameterization would lead to the following problems:

  • Dimensions change with the model. For example, the dimension of the watt unit is [L]²·[M]/[T]³ in the standard model, but it becomes [M]/[T] in the relativistic model. JSR 275 supports custom models for which the number of independent dimensions can be decreased (from seven in the standard model) or increased. (You could add an angular dimension or a monetary dimension, for example.) Modern physics often works with natural units, for which the number of independent dimensions is reduced to zero (all quantities are dimensionless)!
  • Units can have the same dimension and still apply to different quantities. For example both torque and energy have a dimension of [L]²·[M]/[T]² in the standard model. Nevertheless it is convenient and safer to consider them as two different quantities with their own units. Other examples are sea water salinity (PSS-78), which is some kind of concentration, and angles, which are all dimensionless but still convenient to treat as different kinds of quantities. A unit's dimension can be easily retrieved using the Unit.getDimension() method. For example: ELECTRON_VOLT.getDimension() returns [L]²·[M]/[T]². But the physical "type" of a unit can be identified only by its standard units. Consider the REVOLUTION.divide(MINUTE) unit. Its dimension is 1/[T] -- basically the same dimension as frequency. But REVOLUTION.divide(MINUTE).getStandardUnit() returns rad/s, which unequivocally identifies the unit as an angular-velocity unit.

See the Resources section to learn more about dimensional analysis.

A case study: The monetary system

The monetary system is out of JSR 275's scope, but it illustrates how easily the framework can be extended to nonphysical quantities. Such extension can be valuable in the sense that it not only leverages the specification's capabilities (formatting, conversions, and so on) but it compounds its usefulness as well. For example, you can now work with hybrid quantities such as dollar per gallon (gas price) or Euro per square meter (material price) or Yen per kilogram (food price). Figure 3 shows (in green) the new classes and how they attach to the framework.

New classes and how they attach to the framework
Figure 3. Monetary-system extension (click for a larger image)

There's no need to subclass Measure or Measurable. The statements in Listing 13 are supported without further customizations.

Listing 13. Monetary-system integration

 Measure<Double, ?> gazPrice  
    = Measure.valueOf(1.2, EUR.divide(LITRE)); // 1.2 €/L
System.out.println(gazPrice.getDimension());
>> [$]/[L]³

Measure<Double, Money> wallet = Measure.valueOf(13.2, EUR);
Unit<Money> DOLLAR_CENT = USD.compound(CENTI(USD));
System.out.println(wallet.to(DOLLAR_CENT));
>> 18$10¢

Conclusion

Just as parameterization enhanced the Java Collections Framework, the Measures and Units API adds a new dimension to Java primitive types. Whether or not JSR 275 will be included in the Java 7 platform has not yet been decided. We can certainly hope so, because it provides a common framework allowing unit-safe integration of higher-level libraries (such as JSR 310: Date and Time API). In the meantime, you can download the reference implementation from the JScience project.

Acknowledgment

Thanks to Martin Desruisseaux, who wrote the first draft of the specification, and to the expert group for its valuable input and feedback.

Jean-Marie Dautelle holds a master's degree in electrical engineering (Orsay University, France) and a postgraduate degree in information processing and simulation. He is spec lead for JSR 275 and has been a Java Executive Committee member since November 2006. Jean-Marie works for Raytheon in Marlborough, MA on Java-based technology for safety-critical systems. Jean-Marie is the main author of the Javolution and JScience Project libraries.

Learn more about this topic

| 1 2 3 4 Page 4