X

A blog about Oracle Technology Network Japan

クイズに挑戦:2次元配列(中級者向け)

Guest Author

varと2次元配列の併用には注意が必要

著者:Simon Roberts、Mikalai Zaikin

2020年2月27日

その他の設問はこちらから

過去にこのクイズの設問に挑戦したことがある方なら、どれ1つとして簡単な問題はないことをご存じでしょう。クイズの設問は、認定試験の中でも難しいものに合わせています。「中級者向け」「上級者向け」という指定は、設問の難易度ではなく、対応する試験による分類です。しかし、ほとんどすべての場合において、「上級者向け」の方が難しくなります。設問は認定試験対策として作成しており、認定試験と同じルールの適用を意図しています。文章は文字どおりに解釈してください。回答者を引っかけようとする設問ではなく、率直に言語の詳細な知識を試すものだと考えてください。

2次元以上の配列を扱う際にvarを正しく使う能力について確認します。

次のメソッドについて:

public static void main(String[] args) {
    var[][] arr = {{"1", 2},{Integer.valueOf(3)},{4.0}};
    System.out.print(arr[0][1]);
}

どのような結果になりますか。1つ選んでください。

  1. 2
  2. 3
  3. Object@1b0375b
  4. コンパイルが失敗する

この設問は、2次元配列の宣言と初期化、そしてその状況でのvarの使用について問うものです。
varはJava 10で導入されました。厳密に言えば、varはキーワードや型ではありません。Java 11のJava言語仕様のセクション3.9「キーワード」には、「ローカル変数宣言の型として特殊な意味を持つ識別子」と書かれています。便宜上、これを「var疑似タイプ」と呼ぶことがよくあります。

var構文は、特定の状況でローカル変数の宣言と初期化を行う際、一般的な形式の代わりに使用できるものとして導入されました。この構文には、見た目がわかりやすくなるなどのメリットがあります。一般的な形式とは、従来どおり、次のようにして変数の宣言と初期化を行うものです。

int count = 99;

この文は、次のように変更できます。

var count = 99;

この例の場合、3文字の型名intを「見にくい」と判断するのはやや無理があります。しかし、変数によっては非常に冗長になる可能性があります。ネストしたジェネリックを多用した変数などは特にそうです。さらに、状況によっては、プロトタイプのコードを頻繁に変更しながら実験する際に、var構文を使うことで変数の型を変えやすくなります。

var構文は、すべてではなく、特定の状況で使用できると言いました。制限の1つに、varは配列の宣言でベースタイプの代わりには使用できないというものがあります。これは言語仕様での実際の表現ではありませんが、こう表現するのが最適でしょう。この制限に関連する言語仕様のセクションはやや長く、かなり抽象的な構文仕様について述べているからです。興味がある方のためにお伝えすると、セクション14.4「ローカル変数宣言文」に記述されています。この制限は、配列の宣言にはvarを使用できないという意味に誤解されがちです。しかし、正確に言えばそのような内容ではありません。実際の制限は、代入による初期化の左辺に角括弧がある場合、varは使用できないというものです。例で示した方がわかりやすいでしょう。

次に示すのは、通常の形式による有効な初期化です。

int ia[] = new int[]{1,2,3}; // 有効

ただし、次に示すものは、varの有効な使用方法ではありません。代入の左辺に角括弧があることに注意してください。

var ia[] = new int[]{1,2,3}; // 有効でない

しかし、次のコードは許可されています。角括弧がなくなっていることに注意してください。iaに対して推論される型は、「intの配列」という正しい型です。

var ia = new int[]{1,2,3}; // 有効

上記の例には、見慣れない部分が別にあることに注意してください。「new int[]」という部分です。この部分に注目します。次の例も有効でないことに注意してください。

var ia = {1,2,3}; // 有効でない

最後の例が失敗する理由は、配列に明示的なベースタイプが存在しないからです。実はこれは、メソッドの引数リストで簡略形による配列の宣言が許可されていない理由と同じです。例として、次のメソッド宣言について考えてみます。

void doStuff(Object[] oa) {}

次の呼出しは問題ありません。

doStuff(new String[]{"Hello", "Goodbye"}); // 有効

しかし、次のコードは有効ではありません。

doStuff({"Hello", "Goodbye"}); // 有効でない

ここで問題になるのは、皆さんにとってはこの配列が「Stringの配列」であるはずだということは明らかかもしれませんが、コンパイラではそのように確実に推論することはできないという点です。本当に作りたいのはObjectの配列なのに、たまたまStringだけが入っている場合はどうなるでしょうか。不確定要素が存在する場合、コンパイラでは正しく推論することができなくなります。varを使う場合も、配列のベースタイプとして想定されるものは何であるかという、同じ問題が発生します。したがって、varはこのような状況では許可されていません。その結果、設問のコードはコンパイルに失敗するため、選択肢Dが正解で、選択肢A、B、Cは誤りです。

興味のある方は、JEP 286にある詳しい説明をご覧ください。

少し話はそれますが、varの代わりに配列のベースとして使用できる明示的な型は何であるかを考えてみるのもおもしろいものです。ここで必要になるのは、Javaの一般的な型の1つで、右辺の初期化から任意の値を受け取ることができるものです。プリミティブ値をオブジェクトでラップするオートボクシングを考慮すれば、いくつかの型が考えられます。一番わかりやすいのはおそらくjava.lang.Objectですが、java.io.Serializableやjava.lang.Comparableでもよいでしょう。次のようにしてビルドすれば、コード全体が動作するでしょう。

Object [][] arr = {{"1", 2},{Integer.valueOf(3)},{4.0}}; // OK

それでは、この変更を行い、コードのコンパイルが成功している場合、どうなるかを考えてみます。mainメソッドの2行目では、値arr[0][1]を出力しています。今回の場合、arr[0]は{"1", 2}を指しているため、arr[0][1]は値2をラップしているIntegerオブジェクトを指すことになります。したがって、修正した変数宣言を使用すれば、コードから2が出力され、選択肢Aが正解となるでしょう。
ここでオートボクシングの効果に注目するのも興味深いものです。変数宣言の修正後に、次のコードを実行するとします。

System.out.print(arr[0][1].getClass().getCanonicalName());

この実行により、java.lang.Integerが出力されるでしょう。そこから、プリミティブの2がIntegerオブジェクトでボクシングされていることがわかります。

正解は選択肢Dです。

 

Java Magazine 日本版Vol.49の他の記事

Java 14でのJava Flight RecorderとJFR Event Streaming
多くの新機能を搭載したJava 14が登場
Java EEからJakarta EEへ
クイズに挑戦:ラムダ式の型(上級者向け)
クイズに挑戦:2次元配列(中級者向け)
クイズに挑戦:ラムダ式(上級者向け)
クイズに挑戦:関数型インタフェース(上級者向け)


Simon Roberts

Simon Roberts:Sun Microsystemsがイギリスで初めてJavaの研修を行う少し前にSunに入社し、Sun認定Javaプログラマー試験とSun認定Java開発者試験の作成に携わる。複数のJava認定ガイドを執筆し、現在はフリーランスでPearson InformITにおいて録画やライブによるビデオ・トレーニングを行っている(直接またはO'Reilly Safari Books Onlineサービス経由で視聴可能)。OracleのJava認定プロジェクトにも継続的に関わっている。

 

Mikalai Zaikin

Mikalai Zaikin:ベラルーシのミンスクを拠点とするIBA IT ParkのリードJava開発者。OracleによるJava認定試験の作成に携わるとともに、複数のJava認定教科書のテクニカル・レビューを行っている。Kathy Sierra氏とBert Bates氏による有名な学習ガイド『Sun Certified Programmer for Java』では、3版にわたってテクニカル・レビューを務めた。


※本記事は、Simon Roberts、Mikalai Zaikinによる”Quiz Yourself: Two-Dimensional Arrays (Intermediate)“を翻訳したものです。

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.