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進数で表現」 みたいにまどろっこしいことをしないとダメだったと思います (もしかしたらもっと楽な方法はあるのかも知れませんが) けど、なかなか便利になったものです。
読んだ: Java エンジニア養成読本
2014 年 12 月に発売された 『Java エンジニア養成読本 [現場で役立つ最新知識、満載!]』 を読みました。 これだけを読んで Java がわかるわけではないけど、歴史から Java EE、周辺技術に至るまで説明されていて、Java 初心者が全体を俯瞰するのに良さそうだと思いました!
内容紹介
本書は、複数の著者による共著になっています。
まず、巻頭記事がきしださんによる 「誰も教えてくれない Java の世界」。 Java の歴史や、Java のエディション (Java SE、EE、ME について)、JDK や JRE、JCP、JSR の紹介や各種 IDE や有名なフレームワークの紹介など、Java を使い始めてみたけど世界観がよくわからない、という人に役立つ情報が凝縮されています。 「お前に Sun が救えるか」 といったネタも紹介されています。
続く特集 1 が irof さんによる 「Java 入門」。 もちろん Java の構文の細かいところまでは説明されていませんが、クラス定義についてや例外処理、標準ライブラリについてのプラクティスなどが紹介されています。
特集 2 は bitter_fox さんによる 「Java SE 8 時代のデータ処理入門」。 Java SE 8 で導入されたラムダ式や Stream API について説明されます。 私もここら辺のことはまだ詳しくは知らなかったのでためになりました。
特集 3 はキクタローさんによる 「現場で役立つ Java EE」。 Java 初心者にとっては Java EE とは何者なのか良くわからない存在だと思いますが、この特集を読むことでなんとなく何者であるかはわかると思います。 Java EE と一口に言ってもその技術要素は多岐に渡りますが、この特集では Servlet や JSF、JAX-RS、JPA、CDI、EJB といったわりと良く使われる技術要素について主に説明されます。 私も JSF や EJB については詳しくなかったので、本書を読んで勉強になりました。
特集 4 は渡辺さんによる 「現場で役立つチーム開発入門」。 Git や Maven、JUnit によるテストや Jenkins といった、開発現場で良く使われている周辺技術が説明されます。
最後に 「イマドキの Java 受託開発の現場で求められる知識」 というタイトルで、提案や設計について、「web 開発の現場では Java の知識だけでなく、HTML や CSS、JS の知識なども必要である」 みたいな話や、テストやデプロイについて説明されます。
感想
Java 初心者が Java を使った web アプリケーション開発について学ぶのにちょうど良い書籍になっていると思います。 もちろん、前半は web アプリケーション開発とは関係なく Java について学べますし、Java を使うなら (web アプリケーション開発をするつもりがなくても) 読むと良いでしょう。
文体がいいのか構成がいいのかムックという本の形態が良いのかわかりませんが、技術書としては読みやすかったです。
既に Java について深い造詣があるならこの本を読む必要はないと思います *1 が、これから Java を使っていきたい人や最近 Java を学び始めた人は、この本を早い段階で呼んでおくと後々の学習が捗ると思います。 Java の良書はいろいろありますが、重量級なものも多いですので、まずは軽く本書から読んでみる、というのをオススメします。
あなたと Java エンジニア養成読本、今すぐ購入!
Kindle 版もあるようですよ!
Javaエンジニア養成読本 [現場で役立つ最新知識、満載!] (Software Design plus)
- 作者: きしだなおき,のざきひろふみ,吉田真也,菊田洋一,渡辺修司,伊賀敏樹
- 出版社/メーカー: 技術評論社
- 発売日: 2014/11/11
- メディア: 大型本
- この商品を含むブログ (6件) を見る
- 作者: きしだなおき,のざきひろふみ,吉田真也,菊田洋一,渡辺修司,伊賀敏樹
- 出版社/メーカー: 技術評論社
- 発売日: 2014/12/11
- メディア: Kindle版
- この商品を含むブログを見る
関連エントリ
著者
【Retrofit を読む】 利用者が定義したインターフェイスに実装を提供する Java ライブラリの作り方 【リフクレション】
この記事は、はてなエンジニアアドベントカレンダー 2014 の 15 日目のエントリです。 昨日は id:chris4403 による 「開発合宿で何を考えてどう作ったか」 でした。
このエントリでは、Android アプリおよび Java アプリケーション用の REST クライアントライブラリである Retrofit のコードを参照しながら、利用者が定義したインターフェイスの実装を提供するようなライブラリの実装方法について説明します。 主に Java のリフレクションの話になります。
注意点など
- 本記事中に掲載されている Retrofit のコードは、Apache License, Version 2.0 のもとで公開されているものです。
- 記事執筆時点の master ブランチの最新のコミットを参照しています。
- Android アプリ開発者で Retrofit のコードを読みたい人は Android Studio で Retrofit のプロジェクトを開くのが楽で良いと思います。
Retrofit の紹介
Retrofit について簡単に紹介します。 詳しい話は公式サイトの方を見てください。
- 公式サイト : Retrofit
Retrofit は Square によって開発されている REST クライアントライブラリです。 HTTP リクエストを実行するためのメソッドを持ったインターフェイスを定義すると、Retrofit がそのインターフェイスの実装を提供してくれます。 アノテーションにより、HTTP メソッドの指定やどの引数がなんのパラメータであるか、あるいはリクエストボディであるかといったことを指定できます。
下記は Groovy スクリプトではてなハイクの公開タイムラインを取得する REST クライアントのサンプルコード *1 です。 HTTP メソッドの種類とリクエスト先のパスを GET
アノテーション で指定しています。 そして、リクエストごとにクエリパラメータを変更できるように、クエリパラメータの値を引数として受け取るようになっています。
@Grab('com.squareup.retrofit:retrofit:1.8.0') import retrofit.RestAdapter import retrofit.http.GET import retrofit.http.Query // HTTP リクエストを投げるメソッドを持つインターフェイスを定義する。 interface HaikuService { /** はてなハイクの公開タイムラインを取得する */ @GET("/api/statuses/public_timeline.json") List<?> getPublicTimeline(@Query("page") int page, @Query("count") int count) } // 定義したインターフェイスの実装を取得。 RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("http://h.hatena.ne.jp") .build() HaikuService haiku = restAdapter.create(HaikuService.class) // 取得した実装を使用してハイクの公開タイムラインの内容を表示。 def statuses = haiku.getPublicTimeline(1, 10) for (def status : statuses) { println '---' println status.user.name println status.text }
利用者側がインターフェイスを定義して、そのインターフェイスの実装をライブラリが提供する、というのが特徴的ですね。
なお、HTTP 通信の実装や、リクエストボディおよびレスポンスボディと Java オブジェクトの変換機能の実装については Retrofit 自体が持っているわけではありません。 (デフォルトで HTTP 通信の実装はプラットフォームに応じたものが使われ、変換機能には Gson が使われますが、好みに応じてそれらは変更できます。)
利用者が定義したインターフェイスに対して実装を提供するために必要な技術
さて、Retrofit のように利用者が定義したインターフェイスに対して実装を提供するようなライブラリを書くことを考えましょう。 Java では、リフレクションを使用することで実現可能です。
任意のインターフェイスに対して実装を提供するために、Proxy
クラス を使用することができます。 そして、実行されたメソッドの情報を取得するために、リフレクションのための各種メソッドを使用できます。 この 2 つについて順番に説明します。
Proxy
クラス
Proxy
クラスは、動的プロキシクラス (実行時に指定されたインターフェイスを実装するクラス) を生成するためのクラスであり、また、動的クラスのスーパークラスにもなるものです。 実際のメソッド呼び出しの処理は InvocationHandler
インターフェイス を実装したクラスのインスタンスが担います。 Proxy
クラスを使うことで、実行時に任意のインターフェイスに実装を提供することができます。
Retrofit では、retrofit.RestAdapter#create(Class
メソッド の中で動的プロキシクラスのインスタンス生成が行われます。
/** Create an implementation of the API defined by the specified {@code service} interface. */ @SuppressWarnings("unchecked") public <T> T create(Class<T> service) { Utils.validateServiceClass(service); return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new RestHandler(getMethodInfoCache(service))); }
retrofit.RestAdapter#create(Class<T>)
メソッド
上記コードを見ればわかるように、指定されたインターフェイスの実際のメソッド呼び出しを処理するのは retrofit.RestAdapter.RestHandler
クラスです。
リフレクションによるメソッド情報の取得
メソッドが呼ばれた際に実行される HTTP リクエストの内容は、各メソッドに付けられたアノテーションや返り値の型、仮引数の情報によって決まります。 すなわち、メソッドの情報を実行時に見る必要があります。 メソッドの情報を実行時に得るには、Method
クラス に定義されている各種メソッドが使用できます。
Retrofit ではどのような処理になっているのか見ていきましょう。 retrofit.RestAdapter.RestHandler#invoke(Object, Method, Object[])
メソッドの実装は次のようになっています。
@SuppressWarnings("unchecked") // @Override public Object invoke(Object proxy, Method method, final Object[] args) throws Throwable { /* (略) */ // Load or create the details cache for the current method. final RestMethodInfo methodInfo = getMethodInfo(methodDetailsCache, method); if (methodInfo.isSynchronous) { try { return invokeRequest(requestInterceptor, methodInfo, args); } catch (RetrofitError error) { /* (略) */
retrofit.RestAdapter.RestHandler#invoke(Object, Method, Object[])
メソッド
まず、getMethodInfo(Method)
メソッド呼び出しにより指定のメソッドの情報を RestMethodInfo
オブジェクトとして受け取り、その後 invokeRequest
メソッドを呼び出して実際の HTTP リクエスト処理に入ります。 実際には、同期実行か非同期実行か、あるいは RxJava を使っているかで処理が分かれたりしますが、ここではそこら辺の詳細には立ち入りません。
例えば、HTTP リクエストの結果の受け取り方は RestMethodInfo#parseResponseType()
メソッドで解析されます。
private ResponseType parseResponseType() { // Synchronous methods have a non-void return type. // Observable methods have a return type of Observable. Type returnType = method.getGenericReturnType(); // Asynchronous methods should have a Callback type as the last argument. Type lastArgType = null; Class<?> lastArgClass = null; Type[] parameterTypes = method.getGenericParameterTypes(); if (parameterTypes.length > 0) { Type typeToCheck = parameterTypes[parameterTypes.length - 1]; lastArgType = typeToCheck; if (typeToCheck instanceof ParameterizedType) { typeToCheck = ((ParameterizedType) typeToCheck).getRawType(); } if (typeToCheck instanceof Class) { lastArgClass = (Class<?>) typeToCheck; } } /* (略) */
retrofit.RestMethodInfo#parseResponseType()
メソッド
同期実行の場合はメソッドの返り値として HTTP リクエストの結果が返され、非同期実行の場合はコールバックオブジェクトが引数に渡されるようになっているので、下記のメソッドを呼ぶことでメソッドの返り値と仮引数との両方を確認しています。
- 返り値の情報取得 :
Method#getGeneticReturnType()
メソッド - 仮引数の情報取得 :
Method#getGeneticParameterTypes()
メソッド
他にも、メソッドに付けられたアノテーション一覧を取得するために getAnnotations()
メソッド が使われたり、仮引数につけられたアノテーションを取得するために getParameterAnnotations()
メソッド が使われたりしています。
おわりに
この記事では、Retrofit の実装を参照しながら Java のリフレクション機能について紹介しました。 バグの原因になりがちなのでアプリケーションコード中で直接リフレクションを使用するのはできるだけ避けた方がいいと思いますが、リフレクションにより Retrofit のような便利なライブラリを実現することも可能ですので、いい感じに使っていきたいですね。
*1:サンプルコードなのでエラー処理などちゃんと行っていません。
google-http-java-client 入門
Java で HTTP 通信するときのクライアントライブラリを何にするかいつも悩むのですが、最近 google-http-java-client が気になってたのでちょっと使ってみました。 汎用的に HTTP 通信ができればよい、というような用途にはちょうど良さそうです。
数年前からベータ版や RC 版としては存在していましたが、正式にリリースされたのは今年のようです。
google-http-java-client について
Google によって書かれた Java の HTTP クライアントライブラリです。
HTTP トランスポートの抽象化がされており、実際の HTTP 通信を行う低層のライブラリを選択できるのが特徴です。 例えば java.net.HttpURLConnection
を使ったり、Apache HTTP Client を使ったりできます。
また、リクエストやレスポンスのコンテンツボディの XML や JSON のパースやシリアライズを行う機能も含まれており、便利です。
使用できる環境は、Java 5 以降の Java SE 環境や Java EE 環境、Android 1.5 以降などです。
ざっくりとした使い方
準備
使用するためには JAR ファイルをライブラリパスに追加するとか、Maven の依存関係管理に追加するとかする必要があります。 2014 年 11 月 8 日現在の最新バージョンは 1.19.0 で、Maven Central Repository に置かれています。
Gradle を使っているならば次のように依存関係を記述すればよいです。
repositories {
mavenCentral()
}
dependencies {
compile 'com.google.http-client:google-http-client:1.19.0'
}
HTTP リクエストを行う処理の全体の流れ
HttpTransport
オブジェクトを用意する。- これはアプリケーション全体で 1 つだけ存在すればよい。
HttpTransport
のcreateRequestFactory
メソッドを呼んでHttpRequestFactory
オブジェクトを生成する。HttpRequestFactory
からHttpRequest
を生成する。HttpRequest
のexecute
メソッドを呼び出してリクエスト実行、レスポンスとしてHttpResponse
を取得。- レスポンスを処理したあと、終了処理。
サンプルコード
GET リクエストを投げてレスポンスボディを表示する例を Gist に置いてあります。 バージョン 1.19.0 を使用しています。
- サンプルコード: google-http-java-client を使用するサンプルコード
レスポンスのパース
HttpRequest#setParser
メソッド を使って ObjectParser
インターフェイスを実装したインスタンスをパーサーとして登録しておくと、レスポンスのパースを任せることができます。
ライブラリに含まれている ObjectParser
の実装としては、JsonObjectParser
クラス や UrlEncodedParser
クラス があります。
サンプルコード
/* 必要な import 文 import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpResponse; import com.google.api.client.http.UrlEncodedParser; import com.google.api.client.util.GenericData; */ // HttpRequest オブジェクトにパーサーを設定しておく。 // (req は HttpRequest オブジェクト) req.setParser(new UrlEncodedParser()); // リクエスト実行。 HttpResponse res = req.execute(); try { // レスポンスのパースを行う。 (上で設定した UrlEncodedParser が使われる。) GenericData d = res.parseAs(GenericData.class); } finally { // (以下略)
レスポンスのパース後のクラスを作る
上の例ではパース後のクラスとして GenericData
を使用しましたが、どういうパラメータが渡ってくるかわかっている場合、それを受け取るためのクラスを用意しておいて、フィールドに値を設定させることもできます。
例えば、OAuth 1.0 Protocol の Temporary Credentials を取得するためのリクエストのレスポンスをパースする場合を考えてみましょう。 レスポンスの形式は 「oauth_token=xxxxx&oauth_token_secret=xxxxx&oauth_callback_confirmed=true」 というものであることはわかっているので、次のようなクラスでレスポンスを受け取ることができます。 @Key
アノテーション をフィールドに付けることで、パースした結果を受け取るフィールドであることを示しています。
/* 必要な import 文 import com.google.api.client.util.GenericData; import com.google.api.client.util.Key; */ // 必ずしも GenericData を継承する必要はないが、継承しておけばフィールドで定義されていないパラメータも受け取ることができる。 public class OAuthTemporaryCredentialResponse extends GenericData { @Key("oauth_token") public String identifier; @Key("oauth_token_secret") public String sharedSecret; @Key("oauth_callback_confirmed") public String callbackConfirmed; }
そして、パース時にこのクラスを指定することでパース結果を OAuthTemporaryCredentialResponse
オブジェクトとして受け取ることができます。
OAuthTemporaryCredentialResponse d = res.parseAs(OAuthTemporaryCredentialResponse.class); System.out.println("identifier: " + d.identifier); // フィールドアクセスによりレスポンスの値にアクセスできる。
関連
- JavaでのHttpClientサンプル - Starlight : 他の Java の HTTP クライアントの使用例など。
- JavaでのHttpClientサンプル その2 - Starlight : 上のエントリの続編。 google-http-java-client を含むいくつかの HTTP クライアントの使用例が書かれている。
- http://tree-tips.appspot.com/java/googleHttpJavaClient/ : google-http-java-client を使って XML や JSON のパースする例。
「うらがみが Java まわりの ORM を知りたい会」 に参加してきた
Java の O/R マッパーまわりの話を知りたかったので、6/14 に行われた勉強会 「うらがみが Java まわりの ORM を知りたい会」 に参加してきました。 会場は和室でした。
Java まわりの O/R マッパー、あんまり詳しくないのでいろいろ知れて良かったです。 メモを残しておきます。
発表内容
Java の ORM、Doma の話 +α (@backpaper0 さん)
いろんな O/R マッパーについての簡単な紹介と、Doma の紹介。
- 紹介された O/R マッパーのうち、使うとしたら JPA か Iciql か Doma かなーという気持ちになった。 (個人の感想です。)
- ちなみに紹介されてる O/R マッパーのうち私がちゃんと知ってるのは JPA だけ。
- Iciql と H2 Database を組み合わせて Android アプリで使うことができる (実際に製品開発に用いた) とのこと。
Doma について
- 公式サイト: Welcome to Doma — Doma 2.0 ドキュメント
- 2 系は Java 8 以降。 1 系は Java 7 までらしい。
- Doma、なんのライブラリにも依存してないらしい。
- Pluggable Annotation Processing API を使ってる。
- その仕組み上他の言語で使えない。
- 原則としてクエリは SQL ファイルに書かないといけない。 (クエリビルダもあるが使わない方が良いらしい。)
- コンパイル時にいろいろ検出。 (対応する SQL ファイルがなかったり中身が空だったり、アノテーションがなかったりすると)
- DAO の引数や戻り値、エンティティのプロパティ (?) などにドメインクラスを使用できる。 (良い!!!)
- 自動生成されるのはエンティティの補助クラスと DAO の実装クラスと、ドメインクラスの補助クラス。
F 社乙女チームの ORM (黒) 歴史 (@daiksy さん)
- Scala でサーバーサイドを書くにあたっての O/R マッパー選択の歴史。
- Play framework。
- 今は Slick か ScalikeJDBC が良さそうだけど、昔は悩ましかった。
- 最初: Squeryl。
- Implicit conversion を駆使するから IDE の補完が微妙だったりしてしっくりこなかったらしい。
- 2 個目: ScalaQuery (のちの Slick)。
- 使いやすいし、後々デファクトスタンダードになるのは理解できる。
- Scala 2.9 → 2.10 のバージョンアップ時に Slick と名を変えて 2.9 系は切り捨てられた。
- ScalaQuery を使っていた web アプリケーションはあえなくレガシー化...。
- Scala はバイナリ互換性がないのがつらいよね。
- 最後: MapperDao を採用。 (現在も。)
- Squeryl, Slick, Activate, Circumflex-ORM, MapperDao, SORM, Scala ActiveRecord などを検討して、結果的に MapperDao。
- DSL が結構良い。 普通の SQL っぽい感じで書ける。
- ドキュメントの量とかは少ないので、今からなら Slick とか ScalikeJDBC のが良さそうな気がする。
Scala 周りの文化は全然知らないので、そんな感じなのかーと思いながら聞いてた。 Scala 便利だけど互換性とか大変そう。
ORM 初心者が使おうとした (@KYON_MM さん)
3 つの O/R マッパー紹介
- JOOQ
- 機能的には使いやすい。 大体揃っているしユーザー数も多そう。
- QueryDSL を駆逐してやるという勢いを感じる。
- Iciql
- とても薄いラッパー。 手軽で良さそう。
- Slick
- Play Framework のデフォルトになる予定。
- 型型しすぎてる雰囲気。
- Scalaz と同じ雰囲気。
- ScalikeJDBC の方が好きな人が多いのでは?
MyBatis (@s_kozake さん)
GORM めっさゆるい (@kazuhito_m さん)
- リファレンス: 7 Object Relational Mapping (GORM) 2.4.0
- Grails 用の O/R マッパー。
- Grails とは独立させて使おうとすると大変っぽい?
- GORM、Groovy らしさがあふれてるなーと思った。
- ゆるい感じで使いたいならいいけど、まじめに大規模開発に使うと死にそう。