検索
連載

“コンストラクタ”と初期化、本当に理解できてる?【改訂版】Eclipseではじめるプログラミング(13)(3/3 ページ)

これからプログラミングを学習したい方、Javaは難しそうでとっつきづらいという方のためのJavaプログラミング超入門連載です。最新のEclipse 3.4とJava 6を使い大幅に情報量を増やした、連載「Eclipseではじめるプログラミング」の改訂版となります

PC用表示 関連情報
Share
Tweet
LINE
Hatena
前のページへ |       

スーパークラスのコストラクタの呼び出し方

 作成したクラスの動作を確認するクラスを用意します。sample13.ConstructorTestで暗黙にスーパークラスのコンストラクタが呼び出されることを確認し、sample13.ConstructorTest1で明示的なスーパークラスのコンストラクタ呼び出しを確認します。sample13.ConstructorTest2で、それぞれのコンストラクタでスーパークラスのコンストラクタがどのように呼び出されているかを確認します。

package sample13;
public class Sample02 {
    public static void main(String[] args) {
        System.out.println("ConstructorTest():-----");
        ConstructorTest c = new ConstructorTest();
        System.out.println("ConstructorTest1():-----");
        ConstructorTest1 c1 = new ConstructorTest1();
        System.out.println("ConstructorTest2():-----");
        ConstructorTest2 c2 = new ConstructorTest2();
        System.out.println("ConstructorTest2(10):-----");
        c2 = new ConstructorTest2(10);
        System.out.println("ConstructorTest2(10, 5):-----");
        c2 = new ConstructorTest2(10, 5);
    }
}
Sample02.java

 実行結果は、次のとおりです。

ConstructorTest():-----
ConstructorTestBase2:0
ConstructorTestBase2:引数なしのコンストラクタ
ConstructorTest1():-----
ConstructorTestBase1:10
ConstructorTest1:0
ConstructorTest2():-----
……
(略)
Sample02.javaの実行結果

 sample13.ConstructorTestの結果により、暗黙にスーパークラス(sample13.ConstructorTestBase2クラス)の「引数なしのコンストラクタ」が呼び出されることが分かります。sample13.ConstructorTest1では明示的に指定したスーパークラスのコンストラクタ「ConstructorTestBase1(int n)」を呼び出していることが分かります。

コラム 「superと“隠蔽”」

先ほどの結果について、もう少し詳しく見てみましょう。「ConstructorTestBase2:0」の「0」は、どの変数の値だか分かりますか?

「ConstructorTest」から、スーパークラスである「ConstructorTestBase2」の暗黙の「引数なしコンストラクタ呼び出し」をしているので、「ConstructorTestBase2()」が呼び出されています。その中で、「this(0)」により「ConstructorTestBase2(int n)」が呼び出されています。そこでは、「n」が出力されているので、コンストラクタの仮パラメータ「n」の値だというのが正解となります。

ConstructorTestBase2クラスのフィールドnの値を出したいときは、「this.n」とする必要があります。

次に、「ConstructorTestBase1:10」の「10」は、どの変数の値だか分かりますか?

「ConstructorTest1」から、スーパークラスである「ConstructorTestBase1」の「ConstructorTestBase1(int n)」を呼び出していて、そこで「n」が出力されているので、コンストラクタ「ConstructorTestBase1(int n)」の仮パラメータ「n」の値だというのが正解となります。

これ自体は、「ConstructorTestBase2:0」と同じだから大丈夫でしょう。ここでは、このコンストラクタで「this.n = n;」となっているので、フィールドnは10に初期化されている点に注意してください。

では、「ConstructorTest1:0」の「0」は、どの変数の値だか分かりますか?

コードを見ればすぐに分かります。「ConstructorTest1()」内にある「System.out.println("ConstructorTest1:" + n);」で出力されているので、このnです。これは、ConstructorTest1クラスのフィールドnになります。

ConstructorTest1クラスのスーパークラスであるConstructorTestBase1クラスにもフィールドnがあります。前回、「拡張をすると、スーパークラスの実装を継承するため、フィールドも使えるようになる」という説明をしました。しかし、今回のように同じ名前のフィールドがある場合は、スーパークラスの変数は特別な指定をしないと見えなくなります。つまり、「隠蔽 」されて見えなくなってしまうのです。

前述した「シャドウ」と似たような状況です。こういった場合にスーパークラスのフィールドへアクセスするには、どうすればいいのでしょう。

シャドウのときは、thisキーワードを使いました。そのことを思い出し、スーパークラスを参照するにはsuperキーワードを使えばいいことを思い出せば、解答は分かるはずです。スーパークラスのフィールド名とクラスのフィールド名が同じときは、「super.フィールド名」のように指定すれば、スーパークラスのフィールドへアクセスできます。使う機会は少ないかもしれませんが、覚えておきましょう。


暗黙か? 明示か?

 sample13.ConstructorTest2はちょっと量が多いので、1つずつ見ていきましょう。「ConstructorTest2()」のコンストラクタでは、明示的に指定したスーパークラスのコンストラクタ「ConstructorTestBase2()」が呼び出されて、その中で「this(0)」が使われています。その結果「ConstructorTestBase2(int n)」が呼び出されていることが分かります。

ConstructorTest2():-----
ConstructorTestBase2:0
ConstructorTestBase2:引数なしのコンストラクタ
ConstructorTest2:

 「ConstructorTest2(int n)」のコンストラクタでは、明示的に指定したスーパークラスのコンストラクタ「ConstructorTestBase2(int n)」が呼び出されていることが分かります。

ConstructorTest2(10):-----
ConstructorTestBase2:10
ConstructorTest2:10

 「ConstructorTest2(int m, int n)」のコンストラクタでは、thisを使って別のコンストラクタを呼び出しているので、このコンストラクタ内では、スーパークラスのコンストラクタが暗黙に呼び出されていません。「this(m*n)」で「ConstructorTest2(int n)」が呼ばれて、この中で明示的に指定したスーパークラスのコンストラクタ「ConstructorTestBase2(int n)」が呼び出されていることが分かります。

ConstructorTest2(10, 5):-----
ConstructorTestBase2:50
ConstructorTest2:50
ConstructorTest2(m*n):50

Javaのクラスにおける暗黙と当たり前

 クラスを拡張した場合のインスタンス初期化について、詳しく説明しましたが、いかがだったでしょうか。

 結構、覚えておくべきことが多く、大変に感じたかもしれません。しかし、スーパークラスの存在をきちんと意識していれば、クラスの拡張時にコーディングが必要な項目というのは、当たり前だと思われることばかりです。

 暗黙のコンストラクタ呼び出しやjava.lang.Objectを使った暗黙の拡張は、コーディングをしやすくするためのものです。ですから、それほど難しくはないはずです。

初期化の順序は、基本に沿って設計されている

 今回は、インスタンスの初期化について説明しました。スーパークラスで宣言されているフィールドの初期化も含めて考える必要があるため、少し複雑に感じたかもしれません。しかし、どういった順序でインスタンスの初期化をする必要があるかを考えてみると、それほど難しいことはありません。

 「スーパークラスの分を先に初期化してから、自分のクラスの分を初期化する」という基本に沿って、初期化の順序は設計されています。いくつかサンプルを示しましたので、ソースコードと実行結果を比較してみてください。

 「シャドウ」「隠蔽」といった用語もでてきました。フィールド名とコンストラクタの仮パラメータ名に同じものを使った場合や、スーパークラスと同じフィールド名を使った場合に片方の変数が見えなくなってしまうので、注意が必要です。

  今回作ったサンプルのソースコードはこちらからダウンロードできます。次回こそ「アクセス制御」について説明しますので、どうぞお楽しみに。

筆者紹介

小山博史(こやま ひろし)

情報家電、コンピュータと教育の研究に従事する傍ら、オープンソースソフトウェア、Java技術の普及のための活動を行っている。長野県の地域コミュニティである、SSS(G)bugs(J)の活動へも参加している。

著書に「基礎Java」(インプレス)、共著に「Javaコレクションフレームワーク」(ソフトバンククリエイティブ)、そのほかに雑誌執筆多数。



「【改訂版】Eclipseではじめるプログラミング」バックナンバー

Copyright © ITmedia, Inc. All Rights Reserved.

前のページへ |       
ページトップに戻る