Javaでの損失の多い変換

1. 概要

このクイックチュートリアルでは、Javaでの非可逆変換の概念とその背後にある理由について説明します。
同時に、このエラーを回避するための便利な変換テクニックをいくつか検討します。

2. 損失の多い変換

損失の多い変換とは、データの処理中に情報が失われることです。* + *
Javaでは、1つの型を別の型に変換中に*変数の値または精度を失う可能性に対応します。
*大サイズの型の変数を小サイズの型に割り当てようとすると、Javaはエラーを生成します。
たとえば、_long_を_int_に割り当ててみましょう。
long longNum = 10;
int intNum = longNum;
Javaは、このコードのコンパイル中にエラーを出力します。
incompatible types: possible lossy conversion from long to int
ここで、Javaは_long_と_int_に互換性がないことを検出し、変換エラーを発生させます。 _int_の範囲-2,147,483,648〜2,147,483,647の外に_long_値が存在する可能性があるため。
同様に、_float_を_long_に割り当ててみましょう。
float floatNum = 10.12f;
long longNum = floatNum;
incompatible types: possible lossy conversion from float to long
_float_には、対応する_long_値を持たない小数値を含めることができます。 したがって、同じエラーが表示されます。
同様に、_double_番号を_int_に割り当てると、同じエラーが発生します。
double doubleNum = 1.2;
int intNum = doubleNum;
incompatible types: possible lossy conversion from double to int
_double_値は_int_に対して大きすぎるか小さすぎる可能性があり、10進値は変換で失われます。 したがって、潜在的な損失の多い変換です。
また、単純な計算を実行しているときにこのエラーが発生する可能性があります。
int fahrenheit = 100;
int celcius = (fahrenheit - 32) * 5.0 / 9.0;
_double_が_int_と乗算すると、結果は_double_になります。 その結果、これは潜在的な損失の多い変換でもあります。
したがって、* lossy変換の互換性のない型は、異なるサイズまたは型*(整数または小数)を持つことができます。* + *

3. プリミティブデータ型

Javaには、多くのlink:/java-primitives[primitive data types]があり、対応するlink:/java-wrapper-classes[wrapper classes]で利用できます。
次に、可能性のあるすべての非可逆変換の便利なリストをJavaでコンパイルしましょう。
  • short_から_byte_または_char

  • char_から_byte_または_short

  • int_から_byte _、 short_、または_char_

  • long_から_byte _、 short char_、または_int_

  • float_から_byte _、 short char int_、または_long_

  • double_から_byte _、 short char int long_、または_float_

    _short_と_char_は同じサイズであることに注意してください。 それでも、_char_は符号なしデータ型であるため、* _ short_から_char_への変換は非可逆です。

4. 変換テクニック

4.1. プリミティブ型間の変換

link:/java-primitive-conversions [プリミティブの変換]を使用して損失のある変換を回避する簡単な方法は、ダウンキャストです。つまり、大きいサイズのタイプを小さいサイズのタイプにキャストします。 したがって、ナローイングプリミティブ変換とも呼ばれます。
たとえば、downcasting __:__を使用して_a long_数を_short_に変換しましょう
long longNum = 24;
short shortNum = (short) longNum;
assertEquals(24, shortNum);
同様に、_double_を_int_に変換しましょう。
double doubleNum = 15.6;
int integerNum = (int) doubleNum;
assertEquals(15, integerNum);
ただし、大きすぎるまたは小さすぎる値を持つ大きなサイズの型をダウンキャストによって小さなサイズの型に変換すると、予期しない値になる可能性があることに注意してください。
_short_の範囲外の_long_値を変換しましょう:
long largeLongNum = 32768;
short minShortNum = (short) largeLongNum;
assertEquals(-32768, minShortNum);

long smallLongNum = -32769;
short maxShortNum = (short) smallLongNum;
assertEquals(32767, maxShortNum);
*コンバージョンを注意深く分析すると、これらは期待値ではないことがわかります。*
言い換えると、Javaがサイズの大きい型から変換しているときにサイズの小さい型の最高値に達すると、*次の数値はサイズの小さい型の最低値*であり、その逆も同様です。
例を通してこれを理解しましょう。 32768の値を持つ_largeLongNum_が_short_に変換される場合、_shortNum1_の値は-32768 __.__です。_short_の最大値は32767であるため、Javaは_short._の次の最小値に進みます。
同様に、_smallLongNum_が_short_に変換される場合。 _shortNum2_の値は、Javaが_short_の次の最大値に進むため、32767です。
また、_long_の最大値と最小値を_int_に変換するとどうなるか見てみましょう。
long maxLong = Long.MAX_VALUE;
int minInt = (int) maxLong;
assertEquals(-1, minInt);

long minLong = Long.MIN_VALUE;
int maxInt = (int) minLong;
assertEquals(0, maxInt);

4.2. ラッパーオブジェクトとプリミティブ型間の変換

ラッパーオブジェクトをプリミティブに直接変換するには、_intValue()_、_ shortValue()_、_ longValue()_などのラッパークラスでさまざまなメソッドを使用できます。 これは_unboxing_と呼ばれます。
たとえば、_Float_オブジェクトを_long_に変換します。
Float floatNum = 17.564f;
long longNum = floatNum.longValue();
assertEquals(17, longNum);
また、_longValue_または同様のメソッドの実装を見ると、絞り込みプリミティブ変換の使用が見つかります。
public long longValue() {
    return (long) value;
}
ただし、貴重な情報を保存するために、プリミティブ変換の絞り込みを回避する必要がある場合があります。
Double doubleNum = 15.9999;
long longNum = doubleNum.longValue();
assertEquals(15, longNum);
変換後、_longNum_の値は15になります。 ただし、_doubleNum_は15.9999で、16に非常に近い値です。
代わりに、最も近い整数への変換に_Math.round()_を使用できます。
Double doubleNum = 15.9999;
long longNum = Math.round(doubleNum);

assertEquals(16, longNum);

4.3. ラッパーオブジェクト間の変換

このため、すでに説明した変換手法を使用しましょう。
最初に、*ラッパーオブジェクトをプリミティブ値に変換し、ダウンキャストして別のラッパーオブジェクト*に変換します。 つまり、ボックス化解除、ダウンキャスト、およびボックス化のテクニックを実行します。
たとえば、_Double_オブジェクトを_Integer_オブジェクトに変換しましょう。
Double doubleNum = 10.3;
double dbl = doubleNum.doubleValue(); // unboxing
int intgr = (int) dbl; // downcasting
Integer intNum = Integer.valueOf(intgr);
assertEquals(Integer.valueOf(10), intNum);
最後に、_Integer _._ valueOf()_を使用して、プリミティブ型_int_を_Integer_オブジェクトに変換します。 このタイプの変換は、_boxing_と呼ばれます。

5. 結論

この記事では、いくつかの例を使用して、Javaでの非可逆変換の概念を検討しました。 さらに、可能性のあるすべての不可逆変換の便利なリストも作成しました。
途中で、私たちは、プリミティブ変換を縮小し、プリミティブ番号を変換し、損失の多い変換エラーを回避する簡単な手法としてプリミティブ変換を特定しました。
同時に、Javaでの数値変換のための追加の便利なテクニックも検討しました。
この記事のコード実装は、https://github.com/eugenp/tutorials/tree/master/java-numbers-2 [GitHub上]にあります。