|
可変長引数 |
||||
|
||||
C にあって、Java にないものはいろいろあります。struct, union, enum, プリプロセッサなどなど。 struct や union はクラスで代用できるので、別にほしいとは思いません。また、プリプロセッサは人によっては熱望している人もいると思います。携帯電話の Java アプリケーションなんかは機種によってかなり動作が違いますが、機種ごとにソースを作らず、1 つのソースでやりくりしたいときなどは有効だと思います。 enum は C のものよりずっと便利なものが Tiger から導入されました。 それ以外のもので C にあって Java にないものといえば... 可変長引数があります。たとえば、printf の引数はいくら続けても大丈夫ですよね。Java ではこれができないのです。ということは、printf は作れない !! でも、Tiger で printf 相当のものも提供されることになりました。 ということは、可変長引数もできるようになったということです。
|
|
|||||
printf のような文字列をフォーマットして出力するには java.text.MessageFormatt クラスを使います。 MessageFormat クラスも可変長引数であればよかったのですが、今まではそういうわけにはいきませんでした。擬似的に可変長引数を実現するために、引数に Object クラスの配列を使用しています。 たとえば、このように記述します。
arg が引数に使うための配列です。 しかし、この方法だと Object クラスの配列なので、プリミティブ型の変数は使用できません。また、Object クラスなので、どんなオブジェクトを代入することも可能なため、コンパイル時の型チェックができません。 そこで、可変長引数です。 上の例は次のように書きかえられます。
便利だと思いません ? それでは format メソッドはどのように定義されているのでしょう。リファレンスインプリメンテーションのソースを見てみると次のようになっています。
可変長引数にするには、引数の型指定のあとに "..." と 3 つピリオドを続けて記述します。format メソッドの場合は Object の可変長引数になっています。 Object なので、型チェックはできませんが、メソッドの性質上しょうがないでしょう。
|
|
|||||
可変長引数にするには、型指定の後に ... を記述することが分かったので、実際に書いてみましょう。 ところで、可変長引数には制約があるのでしょうか。次のようなコードで確かめてみました。
コンパイルしてみましょう。
はじめのメソッドは第 1 引数で可変長ができるかどうかです。C/C++ の可変長は第 1 引数では使えないのですが、Java ではなんかできるみたいです。でも、今のところ Early Access なので本当はだめなのかもしれません。ということで、怪しいです ^^;; プリミティブでもクラスでも同じように可変長にすることができるようです。 ただし、可変長にするのは最後の引数でないとだめなようです。これは C/C++ でも同じですね。 すっきりしたところで、メソッドを書いてみましょう。 やはり、printf でしょう。でも、ちゃんと書くのは大変なので「えせprintf」です ^^;;
format メソッドが「えせ printf」みたいなメソッドです。先ほど示したように引数には ... をつければいいので int... としています。 宣言は分かったのですが、実際に可変の引数はどのように扱えばいいのでしょう。いいかえれば、引数の 1 つ 1 つにはどうすればアクセスできるのでしょうか。 それには配列を使用します。ここでは int... x とされている引数はメソッドの中では int[] x と int 型の配列として扱うことができます。 配列だと分かればあとは普通に書くだけです。 このアクセスに関しては C/C++ の va_arg などのマクロを使用した方法より全然楽です。 さて、プログラムの中身ですが、pattern の中に % という文字があればそこには数字を出力するようにしているだけです。桁数指定も何もなしです。サンプルなのでゆるしてください。 main メソッドの中で format メソッドを使用していますが、引数に int だけでなく Integer オブジェクトも使用しています。これは Autoboxing を使って勝手に int に変換してくれているため OK なのです。
|
|
||||
というのはメソッドの評価の話しです。 たとえば、同じ名前でオーバロードされたメソッドで、一方が可変長引数、もう一方が通常のメソッドであった場合、どちらがコールされるかということです。 さっそくやってみましょう。
このプログラムは JavaPress Vol.33 の Tiger の紹介記事で使用したものです。 さっそく、実行してみましょう。
1 つめの結果は、可変長引数が選ばれています。可変長引数というのは引数が 0 の場合も含まれるということです。 2 番目は当然の結果です。3 番目、4 番目が通常のメソッドと可変長引数のメソッドの両方に当てはまる場合です。結果を見てみると通常のメソッドがコールされていることが分かります。ということは、通常のメソッドが優先的に評価されるということです。 5 番目からの 3 つはどのような型に評価されるかを見てみたものです。これを見ると、もっとも自分の型に適合するものが選ばれていることが分かります。引数が Integer オブジェクトだったら、Object オブジェクトと評価される前に、Integer オブジェクト、Number オブジェクトと評価されるわけですね。まぁ、当たり前といえば当たり前ですが。
|
|
||||
いつものように、逆コンパイラの Jad を使って調べてみましょう。 逆コンパイルしたのは VarargsSample3 クラスです。肝心の format メソッドの部分は次のようになりました。
変数名が変わっているのは気にしないでください。 さて、引数ですが、なんと配列に置き換わっています。結局、見た目は配列が隠されましたが、裏側ではやっぱり配列を使っているんですね。 メソッドの中で可変長引数の要素をあつかうときに配列としてだったのも、これで納得です。それにしても JSR-201 ではコンパイラが大活躍です。
|
|
||||
MessageFormat クラスが可変長引数に対応したことは前述しましたが、他にも可変長引数に対応したクラスはあるのでしょうか。 そんなときはソースを検索してみましょう。src.zip を解凍して、grep で "..." を探してみました。 そうすると出てきたのがリフレクション関連のクラスです。 たとえば、java.lang.reflect.Method クラスの invoke メソッドの定義は次のようになっています。
Method#invoke メソッドはリフレクションでオブジェクトのメソッドをコールするためのメソッドです。args はそのメソッドの引数になります。任意のメソッドをコールするわけですから、引数を決めるわけにはいかず、可変長にする必要があるのです。 同じように Class#getMethod メソッドや Constructor#newInstance メソッドなども可変長引数になっています。
|
|
||||
可変長引数のメソッドを自分で書くことはほとんどないと思いますが、あれば便利です。 でも一番便利なのは、やっぱり printf です。Tiger で printf 相当のものが提供されることになったのは、とても喜ばしきことです。この機能についてはまた別の項で。
今回使用したサンプルはここからダウンロードできます。 参考
(Dec. 2003) |
|