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


コンストラクタを使って配列を作成する際の微妙な違い

著者:Simon Roberts、Mikalai Zaikin

2019年10月4日

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

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

この設問では、1次元配列を宣言してインスタンス化し、初期化して使用したいと思います。 次のコード・ブロックについて:

    int[] ia = new int[2]; 
    ia[1] = 1;
    for (int v : ia) System.out.print(v + " ");
    String[] sa = new String[2]; 
    sa[0] = "Hi"; 
    for (String v : sa) System.out.print(v + " "); // line n1

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

  1. 0 1 Hi null
  2. null 1 Hi null
  3. 0 1 0 Hi null null
  4. null 1 null Hi null null
  5. 0 1 Hiの後、line n1で例外がスローされる

解答:この設問は、配列の作成と初期化について問うものです。この例では1次元配列が使われていますが、多次元配列(同様に試験対象です)も、本質的には配列の配列にすぎません。そのため、細かいことはいくつか加わりますが、ここでの議論は多次元配列にも基本的には当てはまります。

配列は、2つの方法で作成できます。具体的には、newキーワードを使ったコンストラクタ形式か、または配列リテラルを使うことができます。この設問では、コンストラクタ形式が使われています。この形式では、型の後の角括弧内に、配列の次元を指定する必要があります。 この方法を使う場合、配列に入れる値を同時に指定することはできません。

リテラル形式は波括弧を使うものであり、配列のサイズが小さい場合は特にですが、簡潔な記述となります。この方法では、同時に要素を明示的に初期化することが可能であり、実際には必要になります

配列リテラルには、2つの構文形式が存在します。もっとも一般的なのは、次のようなものです。

new int[] {1, 1}

ここで示されている一般的な形式は、「intの配列」型の式であり、その型の式が必要な、任意の状況で使用できます。そのため、たとえばメソッド呼出しの実パラメータ・リストで使用できます。次に例を示します。

doStuffWithIntArray(new int[] {1, 1});

次のような、単純な代入にも使用できます。

    int[] ia;
    ia = new int[] {1, 1};

上記以外の状況で使用することもできます。

この形式では、(コンストラクタ形式とは違って)角括弧内に配列サイズを含めないことに注意してください。配列のサイズは、初期化する値のリストから推論されます。

2番目の形式の方がおなじみかもしれません。実のところ、この形式は、初期化済みの変数を宣言する際の、値の部分にのみ許可されている省略表記です。この形式を使用できるのは、次のような場合に限られます。

int[] ia = { 1, 1 };

もう1つ注意事項を挙げておきます。変数宣言では、変数名の後に角括弧を記述することも許可されています。しかし、この順序での構文はあまり一般的ではありません。宣言の「型」部分が左側にあり、「名前」の部分が右側にある方が大抵好まれているからです。しかしながら、CやC++というJavaの先駆者で使われてきたものであるため、この形式が思い浮かぶという方もいるかもしれません。次にこの形式の例を示します。

int ia[];

設問に戻ります。今回の設問では、リテラル形式は使われていません。どちらの配列も、コンストラクタ形式を使って作成されています。そのため、生成処理の一部として要素の値を指定することはできず、表現したい特定の値は後で割り当てる必要があります。

もちろん、この設問で行われている明示的な代入では、配列のすべての値が設定されるわけではありません。設問の核心は、配列のうちで明示的な代入が行われていない要素には何の値が入っているかという点です。

実は、この「デフォルトの値は何ですか」という問いは、配列にとどまらず、一般的な状況にも当てはまります。ヒープに割り当てられるすべてのものは、デフォルトの初期化の対象となります。配列であれ、(もっとはっきり言えば)オブジェクトであれ、すべてのオブジェクトがこの対象に含まれます(ここでのポイントは、Javaの配列はオブジェクトであるということです)。このデフォルトの初期化は、メモリ割当てと切り離すことができないものです。コードからヒープに新しいオブジェクトを作成する場合、JVMはデフォルトの初期化が行われたメモリか、例外のいずれかを返します。このデフォルトの初期化が行われていない値を取得することはできません。

それでは、デフォルトの初期化とは何でしょうか。答えは簡単で、ゼロまたはゼロに似た値です。数値型とcharではゼロ、boolean値ではfalse、その他のオブジェクト参照値では、すべてNULLになります。

したがって、new int[2]では、2つのintプリミティブ値の配列の領域が確保されます。そのインデックス(添字と呼ばれることもあります)は0と1(Javaの配列インデックスは、常にゼロベースです)になり、両方の要素に値0が設定されます。そのため、この時点で、ia[0] == 0かつia[1] == 0となります。

次の行では、2つ目の要素(当然、添字は1になります)に値1が代入され、1つ目の要素はデフォルト値0のままになります。

次に、単純なループを使ってint配列の値を出力しています。2つの値、0と1があることはすでに確認しました。そのため、コードのこの部分からの出力は0 1となります(メソッド呼出しがprintlnではなくprintであることに注意してください)。

2つ目の配列も、int配列と同じように宣言され、インスタンス化されます。ここでも、1つの要素が明示的に初期化され、もう1つにはデフォルト値が含まれます。重要な違いは、この配列がオブジェクト型(具体的にはString)であるため、デフォルト値がNULLになることです。そのため、最初の状態でsa[0] == nullかつsa[1] == nullとなります。次に、添字が0の要素が文字列Hiを参照するように代入が行われます。したがって、このコードのループ部分では、Hi nullと出力されます。

以上より、選択肢Aが正解であることがわかります。

int型の配列ではなく、Integerの配列が作成されていた場合、選択肢Bが正解だったでしょう。Integerはオブジェクトで、その配列のデフォルト値はやはりNULLポインタになるからです。しかし、そうではなかったため、選択肢Bは誤りです。

選択肢Cか選択肢Dを選びたくなったのは、new Blah[x]で作成された配列には、添字が0からx-1までのx個の要素ではなく、添字が0からxまでのx+1個の要素があると考えた方かもしれません。

選択肢Eを選びたくなったのは、初期化されていない文字列にアクセスすると例外が発生するだろうと考えた方かもしれません。そう考えるのは、必ずしもおかしなことではありません。興味深いかもしれないのは、式"String is " + sa[1]を評価するとString is nullという出力が得られる一方で、"String is " + sa[1].toString()を評価すると、実はNullPointerExceptionが発生することです。いずれにしても、sa[1]の値はNULLであり、NULLを使ってオブジェクトを参照しようとすると、必ず失敗します。ただし、文字列の連結ではこの問題が回避され、例外がスローされるのではなく、出力nullが得られます。

正解は選択肢Aです。

 

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

Java 13のswitch式と再実装されたSocket APIの内側
Javaにテキスト・ブロックが登場
言語の内側:シールド型
TeaVMを使ってブラウザでJavaを動かす
ツールをよく知る
クイズに挑戦:カスタム例外(上級者向け)
クイズに挑戦:ロケールの読取りと設定(上級者向け)
クイズに挑戦:関数型インタフェース(上級者向け)


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版にわたってテクニカル・レビューを務めた。