スッキリわかるJava入門 第9章 さまざまなクラス機構②
第9章 さまざまなクラス機構②
コンストラクタ 生まれたてのインスタンスの状態
- クラスを用いたプログラミングを始めると、ある「めんどうくささ」を感じる。
public class Main { public static void main(String[] args) { Hero h1 = new Hero(); h1.name = "ミナト"; h1.hp = 100; Hero h2 = new Hero(); h2.name = "アサカ"; h2.hp = 100; Wizard w = new Wizard(); w.name = "スガワラ"; w.hp = 50; w.heal(h1); // ミナトを回復させる(HP: 100 -> 110) w.heal(h2); // アサカを回復させる(HP: 100 -> 110) w.heal(h2); // アサカを回復させる(HP: 110 -> 120) } }
フィールドの初期値
int型,short型,long型 等の数値の型 | 0 |
---|---|
char型(文字) | \u0000 |
boolean型 | false |
int[]などの配列型 | null |
String型などのクラス型 | null |
フィールド初期値を自動設定する
- 実際の開発現場において、大規模な開発を行う場合、1人ですべてを開発することは、まずあり得ない。
- クラスを作るにあたっては、自分以外の開発者がHeroクラスを利用することも考えておかなければならない。
- その開発者が正しくHPに100を代入してくれるとは限らない。
- 「勇者のHPが100であること」は、勇者自身に関する事で、Heroクラスの開発者が一番よく知っていること。(Heroクラス側で責任を持つこと)
- javaでは「インスタンスが生まれた直後に自動実行される処理」をプログラミングできるようになっている。
public class Hero { String name; int hp; Sword sword; public void attack() { System.out.println(this.name + "は攻撃した!"); System.out.println("敵に5ポイントのダメージをあたえた!"); } public Hero() { this.hp = 100; // hpフィールドを100で初期化 } }
- 9行目のHero()メソッドは、このクラスがnewされると、newと同時にメソッド内部で定義しておいた処理が自動的に行われる。
- このようなメソッドをコンストラクタ(constructor)と呼ぶ。
- newされると自動的に実行されてHPに100が代入される。そのためmainメソッド側でHPに初期値を代入する必要はない。
コンストラクタの定義条件
- 「自動実行されるメソッドの条件」を満たすように定義する。
コンストラクタとみなされる条件
- ①メソッド名がクラス名と完全に等しい
- ②メソッド宣言に戻り値が記述されていない(voidもダメ)
コンストラクタに情報を渡す
- HPフィールドは「100」という固定の値で初期化すればよいため、単純なコンストラクタで済んだ。
- 勇者の名前は生み出すインスタンスによって異なる。
- コンストラクタが「毎回異なる追加情報」を引数で受け取れるように宣言する。
public class Hero { String name; int hp; Sword sword; public void attack() { System.out.println(this.name + "は攻撃した!"); System.out.println("敵に5ポイントのダメージをあたえた!"); } public Hero(String name) { this.hp = 100; this.name = name; // 引数の値でnameフィールドを初期化 } }
- このようなHeroクラスを利用する場合は、コンストラクタが実行される際に渡して欲しい引数をnewする際に指定する。
public class Main { public static void main(String[] args) { Hero h = new Hero("ミナト"); System.out.println(h.hp); System.out.println(h.name); } }
2つ以上の同名コンストラクタを定義する
- 現在のHeroクラスには「文字列引数を1つ受け取るコンストラクタ」が定義されている。
- このコンストラクタを作ったことによって、インスタンスを生成するときには、名前を指定する必要が生じた。(new Hero();を実行するとエラーとなる)
- この問題は「引数を受け取らないコンストラクタ」も同時に定義することで解決できる。
public class Hero { String name; int hp; Sword sword; public void attack() { System.out.println(this.name + "は攻撃した!"); System.out.println("敵に5ポイントのダメージをあたえた!"); } public Hero(String name) { this.hp = 100; this.name = name; // 引数の値でnameフィールドを初期化 } public Hero() { this.hp = 100; this.name = "ダミー"; } }
public class Main { public static void main(String[] args) { Hero h1 = new Hero("ミナト"); System.out.println(h1.name); Hero h2 = new Hero(); System.out.println(h2.name); } }
暗黙のコンストラクタ
他のコンストラクタを呼び出す
- 2つ以上のコンストラクタを定義していると、そのうち「重複する処理」が気になる。
- そこで思いつくのが、コンストラクタ②の中で、コンストラクタ①を呼び出す方法。
- Javaではコンストラクタを直接呼び出すことができないため、以下のコードはコンパイルエラーとなる。
public class Hero { String name; int hp; Sword sword; public void attack() { System.out.println(this.name + "は攻撃した!"); System.out.println("敵に5ポイントのダメージをあたえた!"); } public Hero(String name) { // コンストラクタ① this.hp = 100; this.name = name; } public Hero() { // コンストラクタ② this.Hero("ダミー"); } }
別コンストラクタの呼び出しに関するルール
- 「コンストラクタの先頭で、別のコンストラクタを専用の文法を用いて呼び出す場合に限って」特別に許される。
- 「this.クラス名(引数);」と記述する事はできない。
- その代わりに「this(引数);」と記述する。
public class Hero { String name; int hp; Sword sword; public void attack() { System.out.println(this.name + "は攻撃した!"); System.out.println("敵に5ポイントのダメージをあたえた!"); } public Hero(String name) { // コンストラクタ① this.hp = 100; this.name = name; } public Hero() { // コンストラクタ② this("ダミー"); } }