“コンストラクタ”と初期化、本当に理解できてる?:【改訂版】Eclipseではじめるプログラミング(13)(3/3 ページ)
これからプログラミングを学習したい方、Javaは難しそうでとっつきづらいという方のためのJavaプログラミング超入門連載です。最新のEclipse 3.4とJava 6を使い大幅に情報量を増やした、連載「Eclipseではじめるプログラミング」の改訂版となります
スーパークラスのコストラクタの呼び出し方
作成したクラスの動作を確認するクラスを用意します。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); } }
実行結果は、次のとおりです。
ConstructorTest():----- ConstructorTestBase2:0 ConstructorTestBase2:引数なしのコンストラクタ ConstructorTest1():----- ConstructorTestBase1:10 ConstructorTest1:0 ConstructorTest2():----- …… (略)
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コレクションフレームワーク」(ソフトバンククリエイティブ)、そのほかに雑誌執筆多数。
- Javaの例外処理で知らないと損する7つのテクニック
- プログラマの宿命! 例外とエラー処理を理解する
- いまさら聞けない「Javadoc」と「アノテーション」入門
- 7ステップで理解するJavaでの列挙型/enum使用法
- 拡張for文の真の実力を知り、反復処理を使いこなせ
- キュー構造をJavaで実装してジェネリック型を理解する
- 強く型付けされているJavaの理解に必修の“型変換”
- あなたの知らない、4つのマニアックなJava文法
- “ネスト”した型で始める軽量Javaプログラミング!?
- Javaは「抽象クラス」で実装を上手に再利用できる
- 再利用性の高いクラス作成に重要な“アクセス制御”
- “コンストラクタ”と初期化、本当に理解できてる?
- 継承やオーバーライドで簡単にクラスを“拡張”しよう
- 「static」でクラス共有の変数・メソッドを使いこなせ!
- Javaの実案件に必須のパッケージとインポートを知る
- プログラムを「変更」しやすくする“インターフェイス”
- Javaの参照型を文字列操作で理解して文法を総復習
- クラスの振る舞いを表すJavaの“メソッド”とは?
- 複雑なデータを表現できるクラスやフィールドって?
- データ集合を扱うのに便利なJavaの配列と拡張for文
- プログラミングの真骨頂! Javaで“反復処理”を覚える
- プログラミングの醍醐味! Javaで“条件式”を理解する
- Javaで一から理解するプログラムの変数と演算子
- Eclipse 3.4で超簡単Javaプログラミング基礎入門
Copyright © ITmedia, Inc. All Rights Reserved.