ここで、フィールドnを初期化するに当たり「this.n = n;」としています。「this」キーワードは、自分自身を参照する際に使われます。
ここでは、コンストラクタの仮パラメータ名で使われているnと、クラスのフィールドとして定義されている変数nを区別するために、「=」の左辺を「this.n」としています。
コンストラクタやメソッドにおいて、フィールド名と仮パラメータ名に同じ変数名が付いていた場合は、その変数名を使うと、フィールドは見えなくなって、仮パラメータの方が使われます。この状況を「シャドウ」といいます。ですから、自分自身のフィールドへアクセスしたい場合は、自分自身を表す「this」キーワードを使うのです。
引数を取るコンストラクタを定義した場合、「引数なしのコンストラクタ」は暗黙のうちに定義されなくなります。このため、引数なしのコンストラクタが必要な場合は、次のsample13.ConstructorTestBase2クラスのように明示的に定義が必要です。
次の例の場合は、引数なしのコンストラクタと、int型の引数を取るコンストラクタ、2つが定義されています。コンストラクタの最初の処理には、「this呼び出し」が使えます。コードにある「this()」が「this呼び出し」です。ここの例では、引数なしのコンストラクタで、int型の引数を取るコンストラクタを利用しています。
先ほど、「this」キーワードは自分自身を参照することを説明しました。「this呼び出し」も同じように理解しておけばいいでしょう。「this」キーワードを使うと、自分自身にすでに定義されているコンストラクタを呼び出せます。
package sample13; class ConstructorTestBase2 { int n; ConstructorTestBase2() { this(0); System.out.println("ConstructorTestBase2:引数なしのコンストラクタ"); } ConstructorTestBase2(int n) { super(); this.n = n; System.out.println("ConstructorTestBase2:" + n); } }
作成したクラスの動作を確認するクラスを用意します。各クラスのコンストラクタを呼び出しているだけですが、これで、コンストラクタがどういった処理をしているか、様子が分かります。
package sample13; public class Sample01 { public static void main(String[] args) { System.out.println("ConstructorTestBase() :-----"); ConstructorTestBase c = new ConstructorTestBase(); System.out.println("ConstructorTestBase1(1):-----"); ConstructorTestBase1 c1 = new ConstructorTestBase1(1); System.out.println("ConstructorTestBase2() :-----"); ConstructorTestBase2 c2 = new ConstructorTestBase2(); System.out.println("ConstructorTestBase2(5):-----"); c2 = new ConstructorTestBase2(5); } }
実行結果は、次のとおりです。コンストラクタが呼び出されていることが分かります。
ConstructorTestBase() :----- ConstructorTestBase1(1):----- ConstructorTestBase1:1 ConstructorTestBase2() :----- ConstructorTestBase2:0 ConstructorTestBase2:引数なしのコンストラクタ ConstructorTestBase2(5):----- ConstructorTestBase2:5
sample13.ConstructorTestBaseクラスでは、暗黙のコンストラクタにより、コンストラクタを定義しなくてもインスタンスの初期化ができています。sample13.ConstructorTestBase1クラスでは、引数として1を渡したので、それを使って初期化されたことが分かります。
sample13.ConstructorTestBase2クラスの最初の初期化では、引数なしのコンストラクタを呼んだ場合、引数ありのコンストラクタへ0を渡して呼び出したことが分かります。2つ目の初期化では、引数ありのコンストラクタへ5を渡して呼び出したことが分かります。
あるクラスのインスタンス初期化については基本的に、そのクラスにコーディングされたコンストラクタによって決まります。このコンストラクタがどのように実装されているかは、ソースコードを見ない限り分かりません。つまり、スーパークラスで宣言されているフィールドの初期化については、スーパークラスしか知らないのです。
ですから、あるクラスを拡張したクラスを作成する場合は、スーパークラスのコンストラクタを意識する必要があります。最初にスーパークラス内のフィールド初期化をするために、「スーパークラスのコンストラクタからどれかを選んで呼び出した後、拡張した分の初期化をする」という手順を踏む必要があるからです。その初期化が終わってから、自分のクラスのオブジェクトが持つフィールドについて初期化をします。
クラスのインスタンスが生成されたときの初期化の順番については、先ほど説明しました。「スーパークラスのコンストラクタを呼び出す」と、次のように、クラスのインスタンス初期化と同様な順番で初期化がされます。java.lang.Objectクラスのコンストラクタが呼び出されるまで、これが繰り返されます。
ここまで、スーパークラスのコンストラクタを呼び出す処理は明示的に書きませんでしたが、この場合は、スーパークラスの「引数なしのコンストラクタ」が暗黙の内に呼び出されます。
Javaでは、クラス宣言時にextendsを使ってスーパークラスを指定しない場合は、暗黙のうちにjava.lang.Objectクラスを拡張することを説明しました。また、コンストラクタを用意しない場合は、「引数なしのコンストラクタ」が暗黙のうちに用意されることも説明しました。これらを考え合わせると、sample13.ConstructorTestBaseクラスのコードは、次と同じ内容だということになります。
package sample13; class ConstructorTestBase extends java.lang.Object { ConstructorTestBase() { super(); } }
ここで、「super()」というものが出てきました。前回説明した「super」キーワードはスーパークラスのインスタンスを表しますが、「super()」を使うとスーパークラスのコンストラクタを明示的に呼び出せます。スーパークラスに「引数なしのコンストラクタ」がない場合は、明示的にスーパークラスのコンストラクタを呼び出す必要があります。
実際に暗黙のうちにスーパークラスのコンストラクタが呼ばれることを確認するには、次のようなクラスを用意します。ConstructorTestBase2には、「引数なしのコンストラクタ」が定義されていますから、それが呼び出されます。
package sample13; class ConstructorTest extends ConstructorTestBase2 { }
明示的に、super()を指定することもできます。コンストラクタはメソッドではないので継承されませんから、引数ありのコンストラクタで、スーパークラスの同じようなコンストラクタを呼び出したいときは、明示的に呼び出しする必要があります。
package sample13; class ConstructorTest1 extends ConstructorTestBase1 { int n; ConstructorTest1() { super(10); System.out.println("ConstructorTest1:" + n); } }
ついでに、this呼び出しを使った場合にどうなるかも、確認できるようにしておきましょう。
package sample13; class ConstructorTest2 extends ConstructorTestBase2 { int n; ConstructorTest2() { super(); System.out.println("ConstructorTest2:"); } ConstructorTest2(int n) { super(n); System.out.println("ConstructorTest2:" + n); } ConstructorTest2(int m, int n) { this(m*n); System.out.println("ConstructorTest2(m*n):" + m*n); } }
Eclipseでは、スーパークラスからコンストラクタも簡単に生成できます。
このようにEclipseの機能を使うと、スーパークラスからコンストラクタを生成してから、必要な処理を追加するだけで済みます。
次ページでは、スーパークラスのコンストラクタについて、さらに解説します。 “隠蔽”なども出てくるので、混乱するかもしれませんが、じっくりと読んで考えれば理解できると思います。
Copyright © ITmedia, Inc. All Rights Reserved.