Introduzione a javax.measure

1. Panoramica

In questo articolo, introdurremo l'API Unità di misura, che fornisce un modo unificato di rappresentare misure e unità in Java .

Mentre si lavora con un programma contenente quantità fisiche, è necessario rimuovere l'incertezza sulle unità utilizzate. È essenziale che gestiamo sia il numero che la sua unità per evitare errori nei calcoli.

JSR-363 (precedentemente JSR-275 o libreria javax.measure ) ci aiuta a risparmiare il tempo di sviluppo e, allo stesso tempo, rende il codice più leggibile.

2. Dipendenze di Maven

Iniziamo semplicemente con la dipendenza Maven da inserire nella libreria:

 javax.measure unit-api 1.0  

L'ultima versione può essere trovata su Maven Central.

Il progetto unit-api contiene una serie di interfacce che definiscono come lavorare con quantità e unità. Per gli esempi, useremo l'implementazione di riferimento di JSR-363 , che è unit-ri :

 tec.units unit-ri 1.0.3 

3. Esplorazione dell'API

Diamo un'occhiata all'esempio in cui vogliamo immagazzinare l'acqua in un serbatoio.

L'implementazione legacy sarebbe simile a questa:

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

Come si vede, il codice sopra non menziona l'unità di quantità di acqua e non è adatto a calcoli precisi per la presenza del tipo doppio .

Se uno sviluppatore passa erroneamente il valore con un'unità di misura diversa da quella che ci aspettiamo, può portare a gravi errori nei calcoli. Tali errori sono molto difficili da rilevare e risolvere.

L' API JSR-363 ci fornisce le interfacce Quantità e Unità , che risolvono questa confusione e lasciano questo tipo di errori fuori dall'ambito del nostro programma.

3.1. Semplice esempio

Ora, esploriamo e vediamo come questo può essere utile nel nostro esempio.

Come accennato in precedenza, JSR-363 contiene l' interfaccia della quantità che rappresenta una proprietà quantitativa come il volume o l'area. La libreria fornisce numerose interfacce secondarie che modellano gli attributi quantificabili più comunemente usati. Alcuni esempi sono: volume , lunghezza , carica elettrica , energia , temperatura .

Possiamo definire l' oggetto Quantity , che dovrebbe memorizzare la quantità di acqua nel nostro esempio:

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

Oltre all'interfaccia Quantità , possiamo anche utilizzare l' interfaccia Unità per identificare l'unità di misura di una proprietà . Le definizioni delle unità di uso frequente possono essere trovate nella libreria unit-ri , come: KELVIN , METER , NEWTON , CELSIUS .

Un oggetto di tipo Quantitàha metodi per recuperare l'unità e il valore: getUnit () e getValue () .

Vediamo un esempio per impostare il valore della quantità di acqua:

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

Possiamo anche convertire rapidamente questo volume in LITRI in qualsiasi altra unità:

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

Ma, quando proviamo a convertire la quantità di acqua in un'altra unità, che non è di tipo Volume , otteniamo un errore di compilazione:

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

3.2. Parametrizzazione delle classi

Per mantenere la coerenza dimensionale, il framework si avvale naturalmente dei generici.

Le classi e le interfacce sono parametrizzate dal loro tipo di quantità, il che rende possibile il controllo delle nostre unità in fase di compilazione. Il compilatore darà un errore o un avviso in base a ciò che può identificare:

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

C'è sempre la possibilità di bypassare il controllo del tipo usando il metodo asType () :

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

Possiamo anche utilizzare un carattere jolly se non siamo sicuri del tipo di quantità:

Unit kelvinPerSec = KELVIN.divide(SECOND);

4. Conversione di unità

Le unità possono essere recuperate da SystemOfUnits . L'implementazione di riferimento della specifica contiene l' implementazione Units dell'interfaccia che fornisce un insieme di costanti statiche che rappresentano le unità più comunemente usate.

Inoltre, possiamo anche creare un'unità personalizzata completamente nuova o creare un'unità applicando operazioni algebriche su unità esistenti.

Il vantaggio di utilizzare un'unità standard è che non ci imbattiamo nelle insidie ​​della conversione.

Possiamo anche usare prefissi, o moltiplicatori della classe MetricPrefix , come KILO (Unit unit) e CENTI (Unit unit) , che equivalgono rispettivamente a moltiplicare e dividere per una potenza di 10.

Ad esempio, possiamo definire "Chilometro" e "Centimetro" come:

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 questo articolo, abbiamo visto che l' API Unità di misura ci fornisce un comodo modello di misurazione. E, a parte l'utilizzo di Quantità e Unità , abbiamo anche visto quanto sia conveniente convertire un'unità in un'altra, in diversi modi.

Per ulteriori informazioni, puoi sempre controllare il progetto qui.

E, come sempre, l'intero codice è disponibile su GitHub.