Java における byte 型について (あるいはバイナリデータを扱うためのキャスト)
この記事は、もともと次のページに書かれていた内容に、加筆・修正を行ったものです。
はじめに
この記事では、ビット列を角括弧で囲み、[00000000]
のように表現する事にします。
byte
型の表現範囲
Java のプリミティブ型 (基本データ型) の 1 つに byte
型というものがあります。 その名の通り 1 バイト (8 ビット) のサイズの整数値を表現できます。
つまり、[00000000]
から [11111111]
まで表現することができます。
数値でいうと 0 (0x00
) から 255 (0xFF
) まで表現できる、と思うところですが、実際には Java の byte
型は符号付きであり、表現できる範囲は -128 から 127 までとなっています。
The values of the integral types are integers in the following ranges:
Chapter 4. Types, Values, and Variables
- For
byte
, from -128 to 127, inclusive- (略)
バイナリデータを扱うための byte
型のキャスト
符号なしで 0 から 255 の整数を表現したい
上述のとおり byte
型には符号が付いているのですが、バイナリデータの各バイトを 0 (0x00
) から 255 (0xFF
) の整数値で扱いたい場面がたまにあります。 当然 byte
型のままでは 128 以上の整数値を扱えませんので、short
型や int
型にキャストする必要があります。 逆に、short
や int
で表現される 0 から 255 までの整数値を byte
にキャストしたい場面もあるでしょう。 (例えば InputStream#read()
メソッドは、バイトの情報を 0 から 255 までの整数値として返してきます。)
byte
型からの変換
Java SE 8 からは Byte
クラスに便利メソッドが用意されているので、それを使うといいでしょう。 (id:nowokay さんに教えていただきました! ありがとうございます!)
Java SE 7 以下では自分でキャストする必要があります。
キャストする場合 (Java SE 7 以下の場合)
byte
から int
や short
へのキャストは、widening primitive conversion です。 このキャストでは、キャストによって数値の情報が失われることがありません。 (キャスト前とキャスト後で数値として同じ値が表現される。)
バイトを非負整数で表現するという目的においては、単にキャストするだけでは MSB (Most Significant Bit) が 1 の場合 (すなわち、値が負の場合) に、期待する結果にはなりません。
byte valueByte = -1; // ビット列で表すと [11111111] short valueShort = (short) valueByte; // valueShort の値は -1 // ビット列で表すと [1111111111111111]
short
型にしたときにビット列としては [0000000011111111]
となって欲しいところですが、元が負値であるために上位ビットに全て 1 が入ってしまっているわけです。 byte
型をキャストして 0 から 255 の整数値の表現にするには、キャスト後に下位 8 ビット以外を 0 にすれば良いでしょう。
byte valueByte = -1; // ビット列で表すと [11111111] short valueShort = (short) valueByte; // valueShort の値は -1 // ビット列で表すと [1111111111111111] // 下位の 8 ビットだけ残し、残りを全て 0 にする。 valueShort &= 0xFF; // 期待通りの 255 という数値になる // ビット列で表すと [0000000011111111] // キャストと下位 8 ビット以外を削る処理を 1 行で行っても良い。 valueShort = (short) (valueByte & 0xFF);
byte
型へのキャスト
一方で、int
や short
から byte
へのキャストは、narrowing primitive conversion です。 このキャストで整数型を byte
に変換する場合、単純に元の数値を表すビット列の下位 8 ビットがキャスト後の byte
値となります。
なので、0 から 255 の整数値で表現される値を byte
型にする場合は、単純にキャストしてやればよいのです。
short valueShort = 0xFF; // 数値としては 255。 ビット列で表すと [0000000011111111] byte valueByte = (byte) short; // 数値としては -1。 ビット列で表すと [11111111]
byte
値を 16 進数表記の文字列にする
C 言語を使う人にはおなじみの printf
ですが、似たものが Java にもあります。 (Java SE 5 より導入された模様。)
これを使用すれば、簡単に byte
値を 16 進数表記の文字列にできます。 標準出力などに出力して目で確認したい時にはなかなか便利です。
byte b = (byte) 0xE8; // 数値としては -24 // String#format メソッドを使用して文字列取得。 String bStr = String.format("%02X", b); // => "E8" // PrintStream#printf メソッドを使用してそのまま出力する。 System.out.printf("%02X", b);
Java SE 1.4 以前は 「Javaのbyte型を16進数で表現」 みたいにまどろっこしいことをしないとダメだったと思います (もしかしたらもっと楽な方法はあるのかも知れませんが) けど、なかなか便利になったものです。