Inleiding tot javax.measure

1. Overzicht

In dit artikel introduceren we de API voor meeteenheden - die voorziet in een uniforme manier om maten en eenheden in Java weer te geven.

Als we werken met een programma dat fysieke grootheden bevat, moeten we de onzekerheid over de gebruikte eenheden wegnemen. Het is essentieel dat we zowel het nummer als de eenheid beheren om fouten in berekeningen te voorkomen.

JSR-363 (voorheen JSR-275 of javax. maatregel library) helpt ons de ontwikkelingstijd te besparen en maakt tegelijkertijd de code beter leesbaar.

2. Maven afhankelijkheden

Laten we gewoon beginnen met de Maven-afhankelijkheid om de bibliotheek binnen te halen:

 javax.measure unit-api 1.0 

De nieuwste versie is te vinden op Maven Central.

De unit-api project bevat een set interfaces die bepalen hoe met hoeveelheden en eenheden moet worden gewerkt. Voor de voorbeelden gebruiken we de referentie-implementatie van JSR-363, dat is unit-ri:

 tec.units unit-ri 1.0.3 

3. Het verkennen van de API

Laten we eens kijken naar het voorbeeld waarin we water in een tank willen opslaan.

De legacy-implementatie ziet er als volgt uit:

public class WaterTank {public void setWaterQuantity (dubbele hoeveelheid); }

Zoals we kunnen zien, vermeldt de bovenstaande code niet de eenheid van de hoeveelheid water en is deze niet geschikt voor nauwkeurige berekeningen vanwege de aanwezigheid van de dubbele type.

Als een ontwikkelaar de waarde per ongeluk doorgeeft met een andere maateenheid dan we verwachten, kan dit leiden tot ernstige fouten in berekeningen. Dergelijke fouten zijn erg moeilijk op te sporen en op te lossen.

De JSR-363 API biedt ons de Aantal stuks en Eenheid interfaces, die deze verwarring oplossen en dit soort fouten buiten het bereik van ons programma laten.

3.1. Eenvoudig voorbeeld

Laten we nu eens kijken hoe dit nuttig kan zijn in ons voorbeeld.

Zoals eerder gezegd, JSR-363 bevat de Aantal stuks interface die een kwantitatieve eigenschap vertegenwoordigt zoals volume of oppervlakte. De bibliotheek biedt talrijke subinterfaces die de meest gebruikte kwantificeerbare attributen modelleren. Enkele voorbeelden zijn: Volume, Lengte, Elektrische lading, Energie, Temperatuur.

We kunnen de Aantal stuks object, dat de hoeveelheid water in ons voorbeeld zou moeten opslaan:

openbare klasse WaterTank {public void setCapacityMeasure (hoeveelheid capacityMeasure); }

naast de Aantal stuks koppel, we kunnen ook de Eenheid interface om de meeteenheid voor een eigenschap te identificeren. Definities voor vaak gebruikte eenheden zijn te vinden in de unit-ri bibliotheek, zoals: KELVIN, METER, NEWTON, CELSIUS.

Een object van type Aantal stuks heeft methoden voor het ophalen van de eenheid en waarde: getUnit () en getValue ().

Laten we een voorbeeld bekijken om de waarde voor de hoeveelheid water in te stellen:

@Test openbare leegte gegevenQuantity_whenGetUnitAndConvertValue_thenSuccess () {WaterTank waterTank = nieuwe WaterTank (); waterTank.setCapacityMeasure (Quantities.getQuantity (9.2, LITER)); assertEquals (LITER, waterTank.getCapacityMeasure (). getUnit ()); Hoeveelheid waterCapacity = waterTank.getCapacityMeasure (); dubbel volumeInLitre = waterCapacity.getValue (). doubleValue (); assertEquals (9.2, volumeInLitre, 0.0f); }

Dit kunnen we ook omzetten Volume in LITER snel naar een andere eenheid:

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

Maar wanneer we proberen de hoeveelheid water om te zetten in een andere eenheid - die niet van het type is Volume, krijgen we een compilatiefout:

// compilatiefout waterCapacity.to (MetricPrefix.MILLI (KILOGRAM));

3.2. Klasse parametrering

Om de consistentie van de dimensie te behouden, maakt het raamwerk natuurlijk gebruik van generieke geneesmiddelen.

Klassen en interfaces worden geparametriseerd op basis van hun hoeveelheidstype, wat het mogelijk maakt om onze eenheden te laten controleren tijdens het compileren. De compiler geeft een fout of waarschuwing op basis van wat hij kan identificeren:

Eenheid Kilometer = MetricPrefix.KILO (METER); Eenheid Centimeter = MetricPrefix.CENTI (LITER); // compilatiefout

Er is altijd een mogelijkheid om de typecontrole te omzeilen met de asType () methode:

Eenheid inch = CENTI (METER) x (2,54) .asType (lengteklasse);

We kunnen ook een jokerteken gebruiken als we niet zeker zijn van het type hoeveelheid:

Eenheid kelvinPerSec = KELVIN.divide (SECOND);

4. Eenheidsconversie

Eenheids kunnen worden opgehaald uit SystemOfUnits. De referentie-implementatie van de specificatie bevat de Eenheden implementatie van de interface die een set statische constanten biedt die de meest gebruikte eenheden vertegenwoordigen.

Daarnaast kunnen we ook een geheel nieuwe aangepaste eenheid maken of een eenheid maken door algebraïsche bewerkingen op bestaande eenheden toe te passen.

Het voordeel van het gebruik van een standaard unit is dat we niet tegen de valkuilen aanlopen.

We kunnen ook voorvoegsels of vermenigvuldigers van de MetricPrefix klasse, zoals KILO (Eenheidseenheid) en CENTI (Eenheidseenheid), die gelijk zijn aan respectievelijk vermenigvuldigen en delen door een macht van 10.

We kunnen 'Kilometer' en 'Centimeter' bijvoorbeeld definiëren als:

Eenheid Kilometer = MetricPrefix.KILO (METER); Eenheid Centimeter = MetricPrefix.CENTI (METER);

Deze kunnen worden gebruikt wanneer een door ons gewenste unit niet direct leverbaar is.

4.1. Aangepaste eenheden

In elk geval, als een eenheid niet bestaat in het eenhedenstelsel, kunnen we nieuwe eenheden maken met nieuwe symbolen:

  • AlternateUnit - een nieuwe eenheid met dezelfde dimensie maar een ander symbool en een ander karakter
  • Producteenheid - een nieuwe eenheid gecreëerd als het product van rationele krachten van andere eenheden

Laten we enkele aangepaste eenheden maken met behulp van deze klassen. Een voorbeeld van AlternateUnit voor druk:

@Test openbare ongeldig gegevenUnit_whenAlternateUnit_ThenGetAlternateUnit () {Unit PASCAL = NEWTON.divide (METRE.pow (2)) .alternate ("Pa"). AsType (Pressure.class); assertTrue (SimpleUnitFormat.getInstance (). parse ("Pa") .equals (PASCAL)); }

Evenzo een voorbeeld van Producteenheid en zijn conversie:

@Test openbare leegte gegevenUnit_whenProduct_ThenGetProductUnit () {Eenheid squareMetre = METRE.multiply (METER) .asType (Area.class); Hoeveelheidsregel = Quantities.getQuantity (2, METER); assertEquals (line.multiply (line) .getUnit (), squareMetre); }

Hier hebben we een vierkante meter samengestelde eenheid door te vermenigvuldigen METER met zichzelf.

Naast de soorten units biedt het raamwerk ook een UnitConverter class, waarmee we de ene eenheid naar de andere kunnen converteren, of een nieuwe afgeleide eenheid maken met de naam TransformedUnit.

Laten we een voorbeeld bekijken om de eenheid met een dubbele waarde om te zetten van meters naar kilometers:

@Test openbare leegte gegevenMeters_whenConvertToKilometer_ThenConverted () {dubbele afstandInMeters = 50.0; UnitConverter metreToKilometre = METRE.getConverterTo (MetricPrefix.KILO (METER)); dubbele distanceInKilometers = metreToKilometre.convert (distanceInMeters); assertEquals (0,05, afstandInKilometers, 0,00f); }

Om eenduidige elektronische communicatie van hoeveelheden met hun eenheden mogelijk te maken, biedt de bibliotheek de UnitFormat koppel, die systeembrede labels associeert met Eenheden.

Laten we de labels van sommige systeemeenheden controleren met behulp van de SimpleUnitFormat implementatie:

@Test openbare leegte gegevenSymbol_WhenCompareToSystemUnit_ThenSuccess () {assertTrue (SimpleUnitFormat.getInstance (). Parse ("kW") .equals (MetricPrefix.KILO (WATT))); assertTrue (SimpleUnitFormat.getInstance (). parse ("ms") .equals (SECOND.divide (1000))); }

5. Bewerkingen uitvoeren met hoeveelheden

De Aantal stuks interface bevat methoden voor de meest voorkomende wiskundige bewerkingen: toevoegen(), aftrekken(), vermenigvuldigen(), verdelen(). Hiermee kunnen we bewerkingen uitvoeren tussen Aantal stuks voorwerpen:

@Test openbare leegte gegevenUnits_WhenAdd_ThenSuccess () {Totaal aantal = Quantities.getQuantity (2, METER) .add (Quantities.getQuantity (3, METER)); assertEquals (total.getValue (). intValue (), 5); }

De methoden verifiëren ook het Eenheden van de objecten waarmee ze opereren. Als u bijvoorbeeld meters probeert te vermenigvuldigen met liters, resulteert dit in een compilatiefout:

// compilatiefout Hoeveelheid totaal = Quantities.getQuantity (2, METER) .add (Quantities.getQuantity (3, LITER));

Aan de andere kant kunnen twee objecten, uitgedrukt in eenheden met dezelfde dimensie, worden toegevoegd:

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

In dit voorbeeld komen zowel meter- als kilometereenheden overeen met de Lengte dimensie zodat ze kunnen worden toegevoegd. Het resultaat wordt uitgedrukt in de eenheid van het eerste object.

6. Conclusie

In dit artikel hebben we dat gezien Meeteenheden API geeft ons een handig meetmodel. En afgezien van het gebruik van Aantal stuks en Eenheid, we zagen ook hoe gemakkelijk het is om de ene eenheid naar de andere te converteren, op een aantal manieren.

Voor meer informatie kunt u hier altijd het project bekijken.

En, zoals altijd, is de volledige code beschikbaar op GitHub.