1. Overview
1.概述
In this article, we’ll introduce the Units of Measurement API – which provides a unified way of representing measures and units in Java.
在这篇文章中,我们将介绍测量单位API–它提供了一种在Java中表示测量和单位的统一方法。
While working with a program containing physical quantities, we need to remove the uncertainty about units used. It’s essential that we manage both the number and its unit to prevent errors in calculations.
在使用包含物理量的程序时,我们需要消除所用单位的不确定性。我们必须同时管理数字和它的单位,以防止计算中的错误。
JSR-363 (formerly JSR-275 or javax.measure library) helps us save the development time, and at the same time, makes the code more readable.
JSR-363(以前的JSR-275或javax.measure库)帮助我们节省开发时间,同时使代码更易读。
2. Maven Dependencies
2.Maven的依赖性
Let’s simply start with the Maven dependency to pull in the library:
让我们简单地从Maven的依赖性开始,拉入库。
<dependency>
<groupId>javax.measure</groupId>
<artifactId>unit-api</artifactId>
<version>1.0</version>
</dependency>
The latest version can be found over on Maven Central.
最新版本可以在Maven Central上找到。
The unit-api project contains a set of interfaces that define how to work with quantities and units. For the examples, we’ll use the reference implementation of JSR-363, which is unit-ri:
unit-api项目包含一组定义如何处理数量和单位的接口。在示例中,我们将使用JSR-363的参考实现,即unit-ri。
<dependency>
<groupId>tec.units</groupId>
<artifactId>unit-ri</artifactId>
<version>1.0.3</version>
</dependency>
3. Exploring the API
3.探索API
Let’s have a look at the example where we want to store water in a tank.
让我们看看这个例子,我们想把水储存在一个水箱里。
The legacy implementation would look like this:
遗留的实现方式看起来是这样的。
public class WaterTank {
public void setWaterQuantity(double quantity);
}
As we can see, the above code does not mention the unit of quantity of water and is not suitable for precise calculations because of the presence of the double type.
我们可以看到,上述代码没有提到水的数量单位,而且由于double类型的存在,不适合精确计算。
If a developer mistakenly passes the value with a different unit of measure than the one we’re expecting, it can lead to serious errors in calculations. Such errors are very hard to detect and resolve.
如果开发者错误地用与我们期望的不同的计量单位传递数值,就会导致计算的严重错误。这样的错误是很难检测和解决的。
The JSR-363 API provides us with the Quantity and Unit interfaces, which resolve this confusion and leave these kinds of errors out of our program’s scope.
JSR-363 API为我们提供了Quantity和Unit接口,这些接口解决了这个困惑,并将这类错误排除在我们程序的范围之外。
3.1. Simple Example
3.1.简单的例子
Now, let’s explore and see how this can be useful in our example.
现在,让我们探讨一下,看看这在我们的例子中如何有用。
As mentioned earlier, JSR-363 contains the Quantity interface which represents a quantitative property such as volume or area. The library provides numerous sub-interfaces that model the most commonly used quantifiable attributes. Some examples are: Volume, Length, ElectricCharge, Energy, Temperature.
如前所述,JSR-363包含Quantity接口,它表示一个定量属性,如体积或面积。该库提供了许多子接口,用于模拟最常用的可量化属性。一些例子是。体积、长度、电荷、能量、温度。
We can define the Quantity<Volume> object, which should store the quantity of water in our example:
我们可以定义Quantity<Volume>对象,它应该在我们的例子中存储水的数量。
public class WaterTank {
public void setCapacityMeasure(Quantity<Volume> capacityMeasure);
}
Besides the Quantity interface, we can also use the Unit interface to identify the unit of measurement for a property. Definitions for often used units can be found in the unit-ri library, such as: KELVIN, METRE, NEWTON, CELSIUS.
除了Quantity接口,我们还可以使用Unit接口来确定一个属性的测量单位。经常使用的单位的定义可以在unit-ri库中找到,例如。KELVIN, METRE, NEWTON, CELSIUS。
An object of type Quantity<Q extends Quantity<Q>> has methods for retrieving the unit and value: getUnit() and getValue().
一个类型为Quantity<Q extends Quantity<Q>>的对象有检索单位和值的方法。getUnit()/em>和getValue()/em>。
Let’s see an example to set the value for the quantity of water:
让我们看一个设置水量值的例子。
@Test
public void givenQuantity_whenGetUnitAndConvertValue_thenSuccess() {
WaterTank waterTank = new WaterTank();
waterTank.setCapacityMeasure(Quantities.getQuantity(9.2, LITRE));
assertEquals(LITRE, waterTank.getCapacityMeasure().getUnit());
Quantity<Volume> waterCapacity = waterTank.getCapacityMeasure();
double volumeInLitre = waterCapacity.getValue().doubleValue();
assertEquals(9.2, volumeInLitre, 0.0f);
}
We can also convert this Volume in LITRE to any other unit quickly:
我们也可以将这个体积单位LITRE快速转换成任何其他单位。
double volumeInMilliLitre = waterCapacity
.to(MetricPrefix.MILLI(LITRE)).getValue().doubleValue();
assertEquals(9200.0, volumeInMilliLitre, 0.0f);
But, when we try to convert the amount of water into another unit – which is not of type Volume, we get a compilation error:
但是,当我们试图将水的数量转换为另一个单位–不是体积类型,我们得到一个编译错误。
// compilation error
waterCapacity.to(MetricPrefix.MILLI(KILOGRAM));
3.2. Class Parameterization
3.2.类参数化
To maintain the dimension consistency, the framework naturally takes advantage of generics.
为了保持维度的一致性,该框架自然地利用了泛型的优势。
Classes and interfaces are parameterized by their quantity type, which makes it possible to have our units checked at compile time. The compiler will give an error or warning based on what it can identify:
类和接口通过其数量类型进行参数化,这使得在编译时检查我们的单位成为可能。编译器会根据它能识别的内容给出一个错误或警告。
Unit<Length> Kilometer = MetricPrefix.KILO(METRE);
Unit<Length> Centimeter = MetricPrefix.CENTI(LITRE); // compilation error
There’s always a possibility of bypassing the type check using the asType() method:
使用asType()方法总是有可能绕过类型检查的。
Unit<Length> inch = CENTI(METER).times(2.54).asType(Length.class);
We can also use a wildcard if we are not sure of the type of quantity:
如果我们不确定数量的类型,我们也可以使用通配符。
Unit<?> kelvinPerSec = KELVIN.divide(SECOND);
4. Unit Conversion
4.单位转换
Units can be retrieved from SystemOfUnits. The reference implementation of the specification contains the Units implementation of the interface which provides a set of static constants that represent the most commonly used units.
单位可以从SystemOfUnits检索。该规范的参考实现包含接口的Units实现,它提供了一组代表最常用单位的静态常量。
In addition, we can also create an entirely new custom unit or create a unit by applying algebraic operations on existing units.
此外,我们还可以创建一个全新的自定义单元或通过对现有单元进行代数运算来创建一个单元。
The benefit of using a standard unit is that we don’t run into the conversion pitfalls.
使用标准单位的好处是,我们不会遇到转换的陷阱。
We can also use prefixes, or multipliers from the MetricPrefix class, like KILO(Unit<Q> unit) and CENTI(Unit<Q> unit), which are equivalent to multiply and divide by a power of 10 respectively.
我们也可以使用前缀,或者来自MetricPrefix类的乘数,比如KILO(Unit<Q> unit)和CENTI(Unit<Q> unit),它们分别相当于乘和除以10的幂。
For example, we can define “Kilometer” and “Centimeter” as:
例如,我们可以将 “千米 “和 “厘米 “定义为。
Unit<Length> Kilometer = MetricPrefix.KILO(METRE);
Unit<Length> Centimeter = MetricPrefix.CENTI(METRE);
These can be used when a unit we want is not available directly.
当我们想要的单位不能直接获得时,就可以使用这些单位。
4.1. Custom Units
4.1.自定义单位
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:
让我们使用这些类来创建一些自定义单位。一个关于压力的AlternateUnit的例子。
@Test
public void givenUnit_whenAlternateUnit_ThenGetAlternateUnit() {
Unit<Pressure> 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:
同样,一个ProductUnit和其转换的例子。
@Test
public void givenUnit_whenProduct_ThenGetProductUnit() {
Unit<Area> squareMetre = METRE.multiply(METRE).asType(Area.class);
Quantity<Length> line = Quantities.getQuantity(2, METRE);
assertEquals(line.multiply(line).getUnit(), squareMetre);
}
Here, we have created a squareMetre compound unit by multiplying METRE with itself.
在这里,我们通过将METRE与自身相乘,创建了一个squareMetre复合单位。
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.
接下来,对于单位的类型,框架还提供了一个UnitConverter类,它帮助我们将一个单位转换为另一个单位,或者创建一个新的派生单位,称为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.
为了促进数量与其单位的明确电子通信,库提供了UnitFormat接口,它将系统范围内的标签与Units联系起来。
Let’s check the labels of some system units using the SimpleUnitFormat implementation:
让我们使用SimpleUnitFormat实现检查一些系统单位的标签。
@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
5.用数量进行操作
The Quantity interface contains methods for the most common mathematical operations: add(), subtract(), multiply(), divide(). Using these, we can perform operations between Quantity objects:
Quantity接口包含了最常见的数学运算方法。add(), subtract(), multiply(), divide()。使用这些,我们可以在Quantity对象之间进行操作。
@Test
public void givenUnits_WhenAdd_ThenSuccess() {
Quantity<Length> 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:
这些方法还验证它们所操作的对象的Units。例如,试图将米与升相乘将导致一个编译错误。
// compilation error
Quantity<Length> 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<Length> 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
6.结论
In this article, we saw that Units of Measurement API gives us a convenient measurement model. And, apart from the usage of Quantity and Unit, we also saw how convenient it is to convert one unit to another, in a number of ways.
在这篇文章中,我们看到测量单位API为我们提供了一个方便的测量模型。而且,除了Quantity和Unit的用法之外,我们还看到了以多种方式将一个单位转换为另一个单位是多么方便。
For further information, you can always check out the project here.
欲了解更多信息,您可以随时查看这里的项目。
And, as always, the entire code is available over on GitHub.
而且,像往常一样,整个代码都可以在GitHub上找到。