Рубрики
Без рубрики

Введение в javax.measure

Быстрое и очень практичное введение в новые API измерения Java.

Автор оригинала: baeldung.

1. Обзор

В этой статье мы представим API единиц измерения, который обеспечивает унифицированный способ представления мер и единиц измерения в Java .

При работе с программой, содержащей физические величины, нам необходимо устранить неопределенность в отношении используемых единиц измерения. Очень важно, чтобы мы управляли как числом, так и его единицей, чтобы предотвратить ошибки в расчетах.

JSR-363 (ранее JSR-275 или javax.measure библиотека) помогает нам сэкономить время разработки и в то же время делает код более читаемым.

2. Зависимости Maven

Давайте просто начнем с зависимости Maven, чтобы вытащить библиотеку:


    javax.measure
    unit-api
    1.0

Последнюю версию можно найти на странице Maven Central .

Проект unit-api содержит набор интерфейсов, которые определяют, как работать с количествами и единицами измерения. Для примеров мы будем использовать эталонную реализацию JSR-363 , которая является unit-ri :


    tec.units
    unit-ri
    1.0.3

3. Изучение API

Давайте рассмотрим пример, в котором мы хотим хранить воду в резервуаре.

Унаследованная реализация будет выглядеть следующим образом:

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

Как мы видим, приведенный выше код не упоминает единицу количества воды и не подходит для точных расчетов из-за наличия типа double .

Если разработчик ошибочно передает значение с другой единицей измерения, чем та, которую мы ожидаем, это может привести к серьезным ошибкам в расчетах. Такие ошибки очень трудно обнаружить и устранить.

API JSR-363 предоставляет нам интерфейсы Quantity и Unit , которые устраняют эту путаницу и оставляют подобные ошибки вне сферы действия нашей программы.

3.1. Простой Пример

Теперь давайте рассмотрим и посмотрим, как это может быть полезно в нашем примере.

Как упоминалось ранее, JSR-363 содержит интерфейс Quantity , который представляет количественное свойство , такое как объем или площадь. Библиотека предоставляет множество подинтерфейсов, которые моделируют наиболее часто используемые количественные атрибуты. Некоторые примеры: Объем , Длина , Электрический заряд , Энергия , Температура .

Мы можем определить объект Quantity , в котором должно храниться количество воды в нашем примере:

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

Помимо интерфейса Quantity , мы также можем использовать интерфейс Unit для определения единицы измерения для свойства . Определения часто используемых единиц измерения можно найти в библиотеке unit-ri , например: КЕЛЬВИН , МЕТР , НЬЮТОН , ЦЕЛЬСИЙ .

Объект типа Quantity расширяет Quantity> имеет методы для извлечения единицы измерения и значения: getUnit() и GetValue() . расширяет Quantity>

Давайте рассмотрим пример, чтобы установить значение для количества воды:

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

Мы также можем быстро преобразовать этот Объем в литр в любую другую единицу измерения:

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. Преобразование Единиц измерения

Единица измерения s может быть получена из Системы единиц измерения . Эталонная реализация спецификации содержит реализацию интерфейса Units , которая предоставляет набор статических констант, представляющих наиболее часто используемые единицы измерения.

Кроме того, мы также можем создать совершенно новую пользовательскую единицу или создать единицу, применив алгебраические операции к существующим единицам.

Преимущество использования стандартной единицы измерения заключается в том, что мы не сталкиваемся с ловушками преобразования.

Мы также можем использовать префиксы или множители из класса Metric Prefix , такие как KILO(Unit unit) и CENTS(Unit unit) , которые эквивалентны умножению и делению на степень 10 соответственно.

Например, мы можем определить “Километр” и “Сантиметр” как:

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

Их можно использовать, когда нужный нам блок недоступен напрямую.

4.1. Пользовательские Единицы Измерения

В любом случае, если единица не существует в системе единиц, мы можем создать новые единицы с новыми символами:

  • AlternateUnit – новая единица измерения с тем же размером, но другим символом и характером
  • ProductUnit – новая единица, созданная как произведение рациональных мощностей других единиц

Давайте создадим несколько пользовательских единиц, используя эти классы. Пример Альтернативного блока для давления:

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

Аналогично, пример Единицы продукции и ее преобразования:

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

Здесь мы создали квадратный метр составную единицу, умножив МЕТР на себя.

Далее, помимо типов единиц, фреймворк также предоставляет класс Unit Converter , который помогает нам преобразовать одну единицу в другую или создать новую производную единицу под названием TransformedUnit .

Давайте рассмотрим пример, чтобы превратить единицу двойного значения из метров в километры:

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

Для облегчения однозначной электронной связи количеств с их единицами библиотека предоставляет UnitFormat интерфейс , который связывает общесистемные метки с Единицами .

Давайте проверим метки некоторых системных блоков, используя реализацию Simple Unit Format :

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

Методы также проверяют Единицы измерения объектов, с которыми они работают. Например, попытка умножить метры на литры приведет к ошибке компиляции:

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

С другой стороны, можно добавить два объекта, выраженных в единицах измерения, которые имеют одинаковое измерение:

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

В этом примере единицы измерения метр и километр соответствуют измерению Длина , поэтому их можно добавить. Результат выражается в единице первого объекта.

6. Заключение

В этой статье мы увидели, что Units of Measurement API дает нам удобную модель измерения. И, помимо использования Количества и Единицы , мы также увидели, насколько удобно конвертировать одну единицу в другую несколькими способами.

Для получения дополнительной информации вы всегда можете проверить проект здесь .

И, как всегда, весь код доступен на GitHub .