概要

長さ、重量、温度、速度などの単位変換は、物流、製造、科学技術計算などの業務で頻繁に必要になります。変換式を個別に書くと組み合わせ爆発が起きるため、基準単位を経由する設計が有効です。温度変換のように単純な係数乗算では対応できない単位系もあり、華氏と摂氏の変換ミスは海外取引先との仕様書やり取りで実際に起こりやすい問題です。この記事では、enum で単位を定義し、基準単位への変換係数を持たせることで、任意の単位間の変換を共通ロジックで扱う方法を紹介します。

使いどころ

物流システムで重量をkg/lb/ozなど複数単位で入出力する

製造システムで寸法をmm/cm/inchで変換する

科学技術計算で温度をCelsius/Fahrenheit/Kelvinで変換する

コード例

基準単位経由の単位変換
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;

public class UnitConverter {

    enum LengthUnit {
        METER(BigDecimal.ONE),
        CENTIMETER(new BigDecimal("0.01")),
        MILLIMETER(new BigDecimal("0.001")),
        KILOMETER(new BigDecimal("1000")),
        INCH(new BigDecimal("0.0254")),
        FOOT(new BigDecimal("0.3048"));

        final BigDecimal toMeter;

        LengthUnit(BigDecimal toMeter) {
            this.toMeter = toMeter;
        }
    }

    private static final MathContext MC =
        new MathContext(10, RoundingMode.HALF_UP);

    public static BigDecimal convert(
            BigDecimal value,
            LengthUnit from,
            LengthUnit to) {
        var meters = value.multiply(from.toMeter);
        return meters.divide(to.toMeter, MC);
    }

    public static void main(String[] args) {
        var feet = new BigDecimal("6");
        var cm = convert(feet,
            LengthUnit.FOOT, LengthUnit.CENTIMETER);
        System.out.println(feet + " ft = " +
            cm.setScale(2, RoundingMode.HALF_UP) +
            " cm");
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

record + switch 式で変換ロジックを簡潔に書ける。var による型推論で記述量も減る。

Java 17
// Java 17: record + switch 式で変換ロジック
record Conversion(BigDecimal value, String unit) {}
var result = switch (to) {
    case METER      -> meters;
    case CENTIMETER -> meters.divide(
        CENTIMETER.toMeter, MC);
    case INCH       -> meters.divide(
        INCH.toMeter, MC);
};
var output = new Conversion(result, to.name());

Library Comparison

JSR 385 (Units of Measurement)物理量の型安全な演算が必要な場合。外部依存が増える。基本的な変換なら自前で十分。
自前 enum パターン(標準 API)変換対象の単位系が限定的で、係数を enum に持たせるだけで済む場合。依存ゼロで実装でき、拡張も enum 定数の追加だけで完結する。温度のようにオフセット変換が必要な単位系では enum の係数方式だけでは対応できず、個別ロジックの追加が必要になる。

注意点

温度変換は単純な係数乗算ではないため、別途ロジックが必要。

BigDecimal で係数を管理しないと浮動小数点誤差が入る。

単位の追加時は enum にフィールドを追加するだけで拡張できる設計にする。

FAQ

基準単位方式の利点は何ですか?

N種類の単位があっても N×2 の変換メソッドで済み、N×N の組み合わせ爆発を防げます。

温度変換を基準単位方式で扱えますか?

Kelvin を基準にすれば可能ですが、Celsius⇔Fahrenheit のオフセット加算は個別ロジックが必要です。

単位を動的に追加したい場合は?

enum の代わりに Map で変換係数を管理すれば、設定ファイルや DB から読み込めます。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます