Einführung in javax.measure

Einführung in javax.measure

1. Überblick

In diesem Artikel stellen wir die Maßeinheiten-API vor, diea unified way of representing measures and units in Java bereitstellt.

Bei der Arbeit mit einem Programm, das physikalische Größen enthält, müssen wir die Unsicherheit über die verwendeten Einheiten beseitigen. Es ist wichtig, dass wir sowohl die Anzahl als auch die Einheit verwalten, um Fehler bei der Berechnung zu vermeiden.

JSR-363 (früherJSR-275 oderjavax.measure Bibliothek) hilft uns, Entwicklungszeit zu sparen und gleichzeitig den Code besser lesbar zu machen.

2. Maven-Abhängigkeiten

Beginnen wir einfach mit der Maven-Abhängigkeit, um die Bibliothek abzurufen:


    javax.measure
    unit-api
    1.0

Die neueste Version finden Sie überMaven Central.

Das Projektunit-apienthält eine Reihe von Schnittstellen, die definieren, wie mit Mengen und Einheiten gearbeitet wird. Für die Beispiele verwenden wir die Referenzimplementierung vonJSR-363, dhunit-ri:


    tec.units
    unit-ri
    1.0.3

3. Erkunden der API

Schauen wir uns das Beispiel an, in dem wir Wasser in einem Tank speichern möchten.

Die Legacy-Implementierung würde folgendermaßen aussehen:

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

Wie wir sehen können, erwähnt der obige Code nicht die Einheit der Wassermenge und ist aufgrund des Vorhandenseins des Typsdoublenicht für genaue Berechnungen geeignet.

Wenn ein Entwickler den Wert fälschlicherweise mit einer anderen Maßeinheit als der erwarteten übergibt, kann dies zu schwerwiegenden Berechnungsfehlern führen. Solche Fehler sind sehr schwer zu erkennen und zu beheben.

The JSR-363 API provides us with the Quantity and Unit interfaces,, die diese Verwirrung beheben und diese Art von Fehlern aus dem Programmbereich herausnehmen.

3.1. Einfaches Beispiel

Lassen Sie uns nun untersuchen, wie dies in unserem Beispiel nützlich sein kann.

Wie bereits erwähnt, enthältJSR-363the Quantity interface which represents a quantitative property wie Volumen oder Fläche. Die Bibliothek bietet zahlreiche Unterschnittstellen, die die am häufigsten verwendeten quantifizierbaren Attribute modellieren. Einige Beispiele sind:Volume,Length,ElectricCharge,Energy,Temperature.

Wir können dasQuantity<Volume>-Objekt definieren, das die Wassermenge in unserem Beispiel speichern soll:

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

Neben derQuantity-Schnittstelle istwe can also use the Unit interface to identify the unit of measurement for a property. Definitionen für häufig verwendete Einheiten finden Sie in der Bibliothekunit-ri, z. B.:KELVIN,METRE,NEWTON,CELSIUS.

Ein Objekt vom TypQuantity<Q extends Quantity<Q>> verfügt über Methoden zum Abrufen der Einheit und des Werts:getUnit() undgetValue().

Sehen wir uns ein Beispiel an, um den Wert für die Wassermenge festzulegen:

@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);
}

Wir können dieseVolume inLITRE auch schnell in eine andere Einheit umwandeln:

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

Wenn wir jedoch versuchen, die Wassermenge in eine andere Einheit umzuwandeln, die nicht vom TypVolume ist, wird ein Kompilierungsfehler angezeigt:

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

3.2. Klassenparametrierung

Um die Dimensionskonsistenz aufrechtzuerhalten, nutzt das Framework natürlich die Vorteile von Generika.

Klassen und Interfaces werden nach ihrem Quantitätstyp parametrisiert, wodurch es möglich ist, unsere Einheiten bei der Kompilierung überprüfen zu lassen. Der Compiler gibt einen Fehler oder eine Warnung aus, basierend auf dem, was er identifizieren kann:

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

Es besteht immer die Möglichkeit, die Typprüfung mit der MethodeasType()zu umgehen:

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

Wir können auch einen Platzhalter verwenden, wenn wir uns über die Art der Menge nicht sicher sind:

Unit kelvinPerSec = KELVIN.divide(SECOND);

4. Einheitenumrechnung

Units kann ausSystemOfUnits abgerufen werden. Die Referenzimplementierung der Spezifikation enthält dieUnits-Implementierung der Schnittstelle, die eine Reihe statischer Konstanten bereitstellt, die die am häufigsten verwendeten Einheiten darstellen.

Darüber hinaus können wir auch eine völlig neue benutzerdefinierte Einheit erstellen oder eine Einheit erstellen, indem wir algebraische Operationen auf vorhandene Einheiten anwenden.

Der Vorteil der Verwendung einer Standardeinheit besteht darin, dass wir nicht auf die Umrechnungsprobleme stoßen.

Wir können auch Präfixe oder Multiplikatoren aus der KlasseMetricPrefix verwenden, wieKILO(Unit<Q> unit) undCENTI(Unit<Q> unit), die dem Multiplizieren und Dividieren durch eine Potenz von 10 entsprechen.

Zum Beispiel können wir "Kilometer" und "Zentimeter" definieren als:

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

Diese können verwendet werden, wenn eine von uns gewünschte Einheit nicht direkt verfügbar ist.

4.1. Benutzerdefinierte Einheiten

In jedem Fall können wir neue Einheiten mit neuen Symbolen erstellen, wenn im Einheitensystem keine Einheit vorhanden ist:

  • AlternateUnit –ist eine neue Einheit mit derselben Dimension, aber unterschiedlichem Symbol und unterschiedlicher Natur

  • ProductUnit –ist eine neue Einheit, die als Produkt rationaler Kräfte anderer Einheiten geschaffen wurde

Erstellen wir mit diesen Klassen einige benutzerdefinierte Einheiten. Ein Beispiel fürAlternateUnit für Druck:

@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));
}

In ähnlicher Weise ein Beispiel fürProductUnit und seine Umwandlung:

@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);
}

Hier haben wir eine zusammengesetzte Einheit vonsquareMetreerstellt, indem wirMETRE mit sich selbst multipliziert haben.

Neben den Einheitentypen bietet das Framework auch eineUnitConverter-Klasse, mit deren Hilfe wir eine Einheit in eine andere konvertieren oder eine neue abgeleitete Einheit namensTransformedUnit erstellen können.

Sehen wir uns ein Beispiel an, um die Einheit mit einem doppelten Wert von Metern auf Kilometer zu drehen:

@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);
}

Um eine eindeutige elektronische Kommunikation von Mengen mit ihren Einheiten zu ermöglichen, stellt die Bibliothek dieUnitFormat-Schnittstelle, bereit, die systemweite Beschriftungen mitUnits verknüpft.

Überprüfen wir die Beschriftungen einiger Systemeinheiten mithilfe der Implementierung vonSimpleUnitFormat:

@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. Operationen mit Mengen durchführen

Die SchnittstelleQuantity enthält Methoden für die häufigsten mathematischen Operationen:add(),subtract(),multiply(),divide(). Mit diesen können wir Operationen zwischenQuantity Objekten ausführen:

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

Die Methoden überprüfen auch dieUnits der Objekte, mit denen sie arbeiten. Wenn Sie beispielsweise versuchen, Zähler mit Litern zu multiplizieren, tritt ein Kompilierungsfehler auf:

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

Andererseits können zwei Objekte hinzugefügt werden, die in Einheiten mit derselben Dimension ausgedrückt werden:

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

In diesem Beispiel entsprechen sowohl Meter- als auch Kilometereinheiten der Abmessung vonLength, sodass sie hinzugefügt werden können. Das Ergebnis wird in der Einheit des ersten Objekts ausgedrückt.

6. Fazit

In diesem Artikel haben wir gesehen, dassUnits of Measurement API uns ein praktisches Messmodell bietet. Abgesehen von der Verwendung vonQuantity undUnit haben wir auch gesehen, wie bequem es ist, eine Einheit auf verschiedene Weise in eine andere umzuwandeln.

Für weitere Informationen können Sie immerthe project here auschecken.

Und wie immer ist der gesamte Codeover on GitHub verfügbar.