javax.measureの紹介

javax.measureの概要

1. 概要

この記事では、a unified way of representing measures and units in Javaを提供するUnits of MeasurementAPIを紹介します。

物理量を含むプログラムを使用しながら、使用するユニットに関する不確実性を取り除く必要があります。 計算のエラーを防ぐために、数値とその単位の両方を管理することが不可欠です。

JSR-363(以前のJSR-275またはjavax.measureライブラリ)は、開発時間を節約すると同時に、コードを読みやすくするのに役立ちます。

2. Mavenの依存関係

ライブラリをプルするためのMaven依存関係から始めましょう。


    javax.measure
    unit-api
    1.0

最新バージョンはMaven Centralにあります。

unit-apiプロジェクトには、数量と単位の操作方法を定義する一連のインターフェースが含まれています。 例では、unit-riであるJSR-363のリファレンス実装を使用します。


    tec.units
    unit-ri
    1.0.3

3. APIの探索

水をタンクに貯めたい例を見てみましょう。

従来の実装は次のようになります。

public class WaterTank {
    public void setWaterQuantity(double quantity);
}

ご覧のとおり、上記のコードは水の量の単位について言及しておらず、doubleタイプが存在するため、正確な計算には適していません。

開発者が、予想とは異なる測定単位で誤って値を渡した場合、計算で重大なエラーが発生する可能性があります。 このようなエラーを検出して解決するのは非常に困難です。

The JSR-363 API provides us with the Quantity and Unit interfaces,は、この混乱を解決し、これらの種類のエラーをプログラムの範囲外にします。

3.1. 簡単な例

それでは、この例でこれがどのように役立つかを調べてみましょう。

前述のように、JSR-363には、体積や面積などのthe Quantity interface which represents a quantitative propertyが含まれます。 このライブラリは、最も一般的に使用される定量化可能な属性をモデル化する多数のサブインターフェースを提供します。 例としては、VolumeLengthElectricChargeEnergyTemperatureがあります。

Quantity<Volume>オブジェクトを定義できます。このオブジェクトは、次の例で水の量を格納する必要があります。

public class WaterTank {
    public void setCapacityMeasure(Quantity capacityMeasure);
}

Quantityインターフェースに加えて、we can also use the Unit interface to identify the unit of measurement for a property。 頻繁に使用される単位の定義は、KELVINMETRENEWTONCELSIUSなどのunit-riライブラリにあります。

タイプQuantity<Q extends Quantity<Q>>のオブジェクトには、単位と値を取得するためのメソッドがあります:getUnit()およびgetValue()

水の量の値を設定する例を見てみましょう。

@Test
public void givenQuantity_whenGetUnitAndConvertValue_thenSuccess() {
    WaterTank waterTank = new WaterTank();
    waterTank.setCapacityMeasure(Quantities.getQuantity(9.2, LITRE));
    assertEquals(LITRE, waterTank.getCapacityMeasure().getUnit());

    Quantity waterCapacity = waterTank.getCapacityMeasure();
    double volumeInLitre = waterCapacity.getValue().doubleValue();
    assertEquals(9.2, volumeInLitre, 0.0f);
}

LITRE内のこのVolumeを他のユニットにすばやく変換することもできます。

double volumeInMilliLitre = waterCapacity
  .to(MetricPrefix.MILLI(LITRE)).getValue().doubleValue();
assertEquals(9200.0, volumeInMilliLitre, 0.0f);

ただし、水の量をVolumeタイプではない別の単位に変換しようとすると、コンパイルエラーが発生します。

// compilation error
waterCapacity.to(MetricPrefix.MILLI(KILOGRAM));

3.2. クラスのパラメータ化

次元の一貫性を維持するために、フレームワークは当然ジェネリックを利用します。

クラスとインターフェースは数量タイプによってパラメーター化されているため、コンパイル時にユニットをチェックできます。 コンパイラーは、何を識別できるかに基づいてエラーまたは警告を出します。

Unit Kilometer = MetricPrefix.KILO(METRE);
Unit Centimeter = MetricPrefix.CENTI(LITRE); // compilation error

asType()メソッドを使用して型チェックをバイパスする可能性は常にあります。

Unit inch = CENTI(METER).times(2.54).asType(Length.class);

数量のタイプがわからない場合は、ワイルドカードを使用することもできます。

Unit kelvinPerSec = KELVIN.divide(SECOND);

4. 単位変換

UnitsはSystemOfUnitsから取得できます。 仕様のリファレンス実装には、最も一般的に使用される単位を表す一連の静的定数を提供するインターフェイスのUnits実装が含まれています。

さらに、まったく新しいカスタムユニットを作成したり、既存のユニットに代数演算を適用してユニットを作成したりすることもできます。

標準ユニットを使用する利点は、コンバージョンの落とし穴に遭遇しないことです。

接頭辞、またはKILO(Unit<Q> unit)CENTI(Unit<Q> unit)などのMetricPrefixクラスの乗数を使用することもできます。これらは、それぞれ10の累乗で乗算および除算するのと同じです。

たとえば、「キロメートル」と「センチメートル」を次のように定義できます。

Unit Kilometer = MetricPrefix.KILO(METRE);
Unit Centimeter = MetricPrefix.CENTI(METRE);

これらは、必要なユニットが直接利用できない場合に使用できます。

4.1. カスタムユニット

いずれの場合も、単位系に単位が存在しない場合は、新しい記号で新しい単位を作成できます。

  • AlternateUnit –は同じ寸法で、記号と性質が異なる新しい単位

  • ProductUnit –他のユニットの有理数の積として作成された新しいユニット

これらのクラスを使用して、いくつかのカスタムユニットを作成しましょう。 圧力のAlternateUnitの例:

@Test
public void givenUnit_whenAlternateUnit_ThenGetAlternateUnit() {
    Unit PASCAL = NEWTON.divide(METRE.pow(2))
      .alternate("Pa").asType(Pressure.class);
    assertTrue(SimpleUnitFormat.getInstance().parse("Pa")
      .equals(PASCAL));
}

同様に、ProductUnitとその変換の例:

@Test
public void givenUnit_whenProduct_ThenGetProductUnit() {
    Unit squareMetre = METRE.multiply(METRE).asType(Area.class);
    Quantity line = Quantities.getQuantity(2, METRE);
    assertEquals(line.multiply(line).getUnit(), squareMetre);
}

ここでは、METREにそれ自体を乗算して、squareMetre複合ユニットを作成しました。

次に、ユニットのタイプに対して、フレームワークはUnitConverterクラスも提供します。これは、あるユニットを別のユニットに変換したり、TransformedUnitと呼ばれる新しい派生ユニットを作成したりするのに役立ちます。

メートルからキロメートルへのdouble値の単位を変換する例を見てみましょう。

@Test
public void givenMeters_whenConvertToKilometer_ThenConverted() {
    double distanceInMeters = 50.0;
    UnitConverter metreToKilometre = METRE.getConverterTo(MetricPrefix.KILO(METRE));
    double distanceInKilometers = metreToKilometre.convert(distanceInMeters );
    assertEquals(0.05, distanceInKilometers, 0.00f);
}

数量とその単位との明確な電子通信を容易にするために、ライブラリは、システム全体のラベルをUnitsに関連付けるUnitFormatインターフェイス,を提供します。

SimpleUnitFormat実装を使用して、いくつかのシステムユニットのラベルを確認しましょう。

@Test
public void givenSymbol_WhenCompareToSystemUnit_ThenSuccess() {
    assertTrue(SimpleUnitFormat.getInstance().parse("kW")
      .equals(MetricPrefix.KILO(WATT)));
    assertTrue(SimpleUnitFormat.getInstance().parse("ms")
      .equals(SECOND.divide(1000)));
}

5. 数量を使用した操作の実行

Quantityインターフェースには、最も一般的な数学演算のメソッドが含まれています:add()subtract()multiply()divide()。 これらを使用して、Quantityオブジェクト間で操作を実行できます。

@Test
public void givenUnits_WhenAdd_ThenSuccess() {
    Quantity total = Quantities.getQuantity(2, METRE)
      .add(Quantities.getQuantity(3, METRE));
    assertEquals(total.getValue().intValue(), 5);
}

これらのメソッドは、操作しているオブジェクトのUnitsも検証します。 たとえば、メートルをリットルで乗算しようとすると、コンパイルエラーが発生します。

// compilation error
Quantity total = Quantities.getQuantity(2, METRE)
  .add(Quantities.getQuantity(3, LITRE));

一方、同じ次元を持つ単位で表現された2つのオブジェクトを追加できます。

Quantity totalKm = Quantities.getQuantity(2, METRE)
  .add(Quantities.getQuantity(3, MetricPrefix.KILO(METRE)));
assertEquals(totalKm.getValue().intValue(), 3002);

この例では、メートルとキロメートルの両方の単位がLength次元に対応しているため、追加できます。 結果は、最初のオブジェクトの単位で表されます。

6. 結論

この記事では、Units of Measurement APIが便利な測定モデルを提供することを確認しました。 また、QuantityUnitの使用法とは別に、さまざまな方法で1つの単位を別の単位に変換することがいかに便利であるかもわかりました。

詳細については、いつでもthe project hereをチェックアウトできます。

そして、いつものように、コード全体が利用可能ですover on GitHub