Einführung in javax.measure

1. Übersicht

In diesem Artikel stellen wir die API für Maßeinheiten vor, die eine einheitliche Darstellung von Maßen und Einheiten in Java bietet .

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üher JSR-275 oder javax.measure library) 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 auf Maven Central.

Das unit-api- Projekt enthält eine Reihe von Schnittstellen, die definieren, wie mit Mengen und Einheiten gearbeitet wird. Für die Beispiele verwenden wir die Referenzimplementierung von JSR-363 , die unit-ri ist :

 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, ist der obige Code nicht die Einheit der Wassermenge erwähnt und ist nicht geeignet für präzise Berechnungen wegen der Anwesenheit des Doppeltypen.

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.

Die JSR-363- API stellt uns die Schnittstellen für Menge und Einheit zur Verfügung , 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ält JSR-363 die Mengenschnittstelle , die eine quantitative Eigenschaft wie Volumen oder Fläche darstellt. Die Bibliothek bietet zahlreiche Unterschnittstellen, die die am häufigsten verwendeten quantifizierbaren Attribute modellieren. Einige Beispiele sind: Volumen , Länge , elektrische Ladung , Energie , Temperatur .

In unserem Beispiel können wir das Mengenobjekt definieren , das die Wassermenge speichern soll:

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

Neben der Mengenschnittstelle können wir auch die Einheitenschnittstelle verwenden, um die Maßeinheit für eine Eigenschaft zu identifizieren . Definitionen für häufig verwendete Einheiten finden Sie in der unit-ri- Bibliothek, z. B .: KELVIN , METER , NEWTON , CELSIUS .

Ein Objekt vom Typ Mengeverfügt über Methoden zum Abrufen der Einheit und des Werts: getUnit () und getValue () .

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 dieses Volume in LITER auch schnell in eine andere Einheit konvertieren :

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 Typ Volume ist, wird ein Kompilierungsfehler angezeigt:

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

3.2. Klassenparametrierung

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

Klassen und Schnittstellen werden nach ihrer Mengenart parametrisiert, wodurch unsere Einheiten zur Kompilierungszeit überprüft werden können. 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 asType () -Methode 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

Einheiten können aus SystemOfUnits abgerufen werden . Die Referenzimplementierung der Spezifikation enthält die Einheitenimplementierung 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 Konvertierungsprobleme stoßen.

Wir können auch Präfixe oder Multiplikatoren aus der MetricPrefix- Klasse verwenden, z. B. KILO (Einheit Einheit) und CENTI (Einheit Einheit) , die dem Multiplizieren und Teilen durch eine Potenz von 10 entsprechen.

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

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

These can be used when a unit we want is not available directly.

4.1. Custom Units

In any case, if a unit doesn't exist in the system of units, we can create new units with new symbols:

  • AlternateUnit – a new unit with the same dimension but different symbol and nature
  • ProductUnit – a new unit created as the product of rational powers of other units

Let's create some custom units using these classes. An example of AlternateUnit for pressure:

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

Similarly, an example of ProductUnit and its conversion:

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

Here, we have created a squareMetre compound unit by multiplying METRE with itself.

Next, to the types of units, the framework also provides a UnitConverter class, which helps us convert one unit to another, or create a new derived unit called TransformedUnit.

Let's see an example to turn the unit of a double value, from meters to kilometers:

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

To facilitate unambiguous electronic communication of quantities with their units, the library provides the UnitFormat interface, which associates system-wide labels with Units.

Let's check the labels of some system units using the SimpleUnitFormat implementation:

@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. Performing Operations With Quantities

The Quantity interface contains methods for the most common mathematical operations: add(), subtract(), multiply(), divide(). Using these, we can perform operations between Quantity objects:

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

The methods also verify the Units of the objects they are operating on. For example, trying to multiply meters with liters will result in a compilation error:

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

On the other hand, two objects expressed in units that have the same dimension can be added:

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

In this example, both meter and kilometer units correspond to the Length dimension so they can be added. The result is expressed in the unit of the first object.

6. Conclusion

In diesem Artikel haben wir gesehen, dass die API für Maßeinheiten ein praktisches Messmodell bietet. Abgesehen von der Verwendung von Menge und Einheit haben wir auch gesehen, wie bequem es ist, eine Einheit auf verschiedene Weise in eine andere umzuwandeln.

Weitere Informationen finden Sie jederzeit hier.

Und wie immer ist der gesamte Code auf GitHub verfügbar.