【Java進化史 番外編】Java8で「符号なし」が追加?本当か?
そういえば。
Java8で「符号なし」が導入された、と どこかで聞いたことがある。
……え?
Javaって、unsigned無かったよね?
なんで今頃?
ちょっと気になったので、調べてみた。
■ きっかけは、IPアドレス
昔、こんなコードを書いたことがある。
InetAddress addr = InetAddress.getLocalHost();
byte[] ip = addr.getAddress();
System.out.println(ip[0]);
出力:
-64
……は?
IPアドレスって、192とか168じゃなかったか?
なぜマイナスになる?
■ 192って何なのか
例として、
192.168.0.1
この「192」は、 IPアドレスの最初の1バイト(8ビット)である。
IPv4は32ビット。
8ビット × 4 = 32ビット
つまり、IPアドレスは4つのbyteでできている。
192を2進数にすると:
11000000
これが1バイトの正体だ。
■ なぜ -64 になるのか
Javaのbyteは符号付き。
-128 ~ 127
11000000を符号付きとして解釈すると、 それは -64 になる。
それだけの話である。
■ Java8ならどう書く?
IP取得方法は変わっていない。 InetAddressも昔のままだ。
変わったのは、読み方である。
InetAddress addr = InetAddress.getLocalHost();
byte[] ip = addr.getAddress();
int first = Byte.toUnsignedInt(ip[0]);
System.out.println(first);
Java8で追加された Byte.toUnsignedInt() によって、
符号なしとして読むことができるようになった。
■ 4つ並べると
昔
for (byte b : ip) {
System.out.print(b + ".");
}
-64.-88.0.1.
Java8
for (byte b : ip) {
int value = Byte.toUnsignedInt(b);
System.out.print(value + ".");
}
192.168.0.1.
■ で、結局なにが起きたのか
Java8は、unsigned型を追加したわけではない。
unsignedとして「読む方法」を追加した。
byteの仕様は変わっていない。 intの仕様も変わっていない。
もちろん、boxingやラッパークラスの挙動もそのままである。
既存のコードを一切壊さない。
その範囲でできることを追加した。
値そのものは昔から同じ。
型は増やさず、 解釈の手段だけを加えた。
■ なぜ今頃?(少しだけ補足)
Javaは当初、型を増やさない設計を選んだ。
だからunsignedは入らなかった。
だが、 ネットワークやビット演算の現場では、 0~255として扱いたい場面が多かった。
そこでJava8で、 型は増やさず、解釈メソッドを追加した。
いかにもJavaらしい、 後方互換を守る現実解である。