Pull to refresh

Работа с единицами системы СИ на Java

Level of difficultyMedium
Reading time4 min
Views2.9K
Система СИ и взаимосвязи между единицами физических величин.
Система СИ и взаимосвязи между единицами физических величин.

Библиотека по работе с единицами системы СИ KotUniL, разработанная изначально на Kotlin, недавно сделана мультиплатформенной. В частности, она доступна теперь и на JavaScript, о чём написано здесь.

А зачем нужна эта библиотека?

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

Эта проблема реальная и уже доказано приводила к авариям, обошедшимся во многие миллионы долларов. (Одна из историй здесь).

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

Одна из таких библиотек - KotUniL (si-units).

Использование технологии Kotlin Multiplatform позволяет из кода на Kotlin получить код для целого ряда платформ, в том числе для JVM.
А это означает, что тем самым реализованную в библиотеке функциональность можно использовать из Java-программ. (Строго говоря, этого можно было достичь и не используя Kotlin Multiplatform, но в данном случае мы получаем JVM-библиотеки автоматически вместе с другими вариантами, что экономит наши усилия).

Как её использовать?

А как использовать библиотеку из Java?
Подключить библиотеку в ваш проект можно как dependency:

repositories {
    mavenCentral()
}

dependencies {
    implementation("eu.sirotin.kotunil:kotunil-jvm:<version>")
}

На момент написания статьи последняя версия была 4.1.1

Какие различия между вариантами на Kotlin и Java существуют?

А дальше всё тоже самое, как в Kotlin? К сожалению, есть два важных отличия.

Во первых, Java не поддерживает operator overloading и поэтому код смотрится менее элегантно, чем на Котлин.

Рассмотри, например, такую задачку: Маша протирала снаружи стекло аквариума, задела стоявшую рядом вазу, в результате чего стекло аквариума разбилось и вода вытекла на пол. В аквариуме до этой неприятности было 32 литра воды. Комната Маши имеет длину 4 метра и ширину 4,3 метра. На какой высоте в мм. находится сейчас вода в комнате, при условии, что она осталась там и не вытекла?

На Котлине решение выглядит таким образом:

val s = 4.m * 4.3.m
val h = 32.l/s 

А на Java нам приходится вместо знаков арифметических операций над размерными единицами использовать функции:

Expression s = m.times(4).times(m.times(4.3));
Expression v = L.times(32);
Expression h = v.div(s)

Однако все остальные прелести KotUniL сохранены. Амперы с секундами складывать нельзя, а перемножать и делить можно.

Во-вторых, трансформируя код Kotlin в код на Java, транспилер создаёт для классов библиотеки из одного Kotlin-класса два Java-класса. Поэтому, например, работая с метрами мы должны в Java-импорты добавлять строку:

import static eu.sirotin.kotunil.base.MetreKt.*;

Дальнейшую информацию о KotUniL вы надаете в репозитории на GitHub ( см. подробный пример работы с единицами системы СИ в Java в модуле app/jvm/java-console).

Про теоретические основы и особенности использования KotUniL вы можете прочитать и в этой серии стаей: 

  1. Магия размерностей и магия Котлина. Часть первая: Введение в KotUniL  

  2. Магия размерностей и магия Котлина. Часть вторая: Продвинутые возможности  KotUniL

  3. Магия размерностей и магия Котлина. Часть третья: Смешение магий

А есть ли альтернативы?

Я разработал KotUniL, поскольку ни одна из существовавших на тот момент библиотек меня не удовлетворяла. В Java сообществе существует JSR 385. Очевидным образом у разработчиков этой спецификации были схожие с моими мотивации. Но там разработчики пошли по пути выделения размерностей. У них получились Quantity<Volume>Unit<Length> и так далее. Разумеется, всех возможных комбинаций таким образом не покрыть, но множеству приложений этого делать и не придётся.

Вот статья на Baeldung про общий вид работы с библиотекой.

Дизайн библиотеки мне не понравился. Чего только стоит подобный пример конвертирования метров в километры, взятый мной из рекомендованной выше статьи!:

double distanceInMeters = 50.0;
UnitConverter metreToKilometre = METRE.getConverterTo(MetricPrefix.KILO(METRE));
double distanceInKilometers = metreToKilometre.convert(distanceInMeters );

Используя KotUniL вы это запишите так:

val d = 50.m
val x = d.km

Я понимаю, преимущество JSR 385 состоит в обнаружении ошибок типизации на этапе компиляции, а не в наглядности краткости кода. В этой статье я показываю, что любые формулы KotUniL проверяются на ошибки типизации ровно одним юнит-тестом при наглядности кода, схожей с наглядностью технических и научных статей.

Почему стандарт СИ не реализован в Kotlin?

Итак, в мире Java существует по крайней мере спецификация JSR 385, касающаяся реализации обработки единиц системы СИ, а как обстоят с эти дела в Kotlin?

К сожалению, в числе существующих и разрабатываемых стандартных библиотек Kotlin такой библиотеки пока нет.

Я предложил разработчикам языка включить эту функциональность (не обязательно KotUniL) в состав библиотек, поставляемых вместе с языком: (KT-55556). Но пока не убедил их. Но возможно, если вы, уважаемые читатели, активно проплюсуете это предложение ( а заодно и KotUniL на GitHub :-), то ситуация исправится.

Tags:
Hubs:
Total votes 11: ↑5 and ↓60
Comments42

Articles