引数を受け取るコンストラクタ
コンストラクタの機能
メインメソッドでインスタンス化をした時に毎回同じ情報が自動的に入る
ただ自動的に入れてほしい情報が「勇者の名前」のように毎回同じ情報ではない時は?
("太郎"、"次郎"のように名前は毎回違うが、「名前を代入する」という行為はインスタンスを作るたびに毎回行うので、
毎回コードを書くより自動化させた方が効率的)
コンストラクタが「毎回異なる追加情報」を
引数(インスタンスから渡される情報)として受け取れるように宣言を書く
ex. public class Hero{ // Heroインスタンスの金型となるクラス
String name;
int hp;
Hero(String name) { //コンストラクタを定義(※名前はクラス名と一緒)
↑引数として文字列を受け取れるように宣言
(インスタンス化される時に"太郎"や"次郎"などの情報が代入され、実行される)
this.name=name:
↑
ブロックの外側のフィールド(this.name)に引数で渡されてきた情報(name)が
自動で代入されるように指定 ※thisは外側を表す
this.hp=100;
}
}
インスタンス化する時にクラスのコンストラクタに情報を渡すには、
newする際に指定する
public class Main{ //指示クラス
public static void main(String[ ] args){ //指示メソッド
Hero h=new Hero("太郎");
※ ただこれをすると必ずnewでインスタンスを生成した時に引数を入れないと
エラーになる
(引数なしのコンストラクタは宣言してないから)
ex. Hero h=new Hero( ); →実行時エラー
この問題は「引数を受け取らないコンストラクタ」も同時に定義することで
解決出来る
ex. public class Hero{
public static void main(String[ ] args){
String name:
int hp;
Hero(String name){
….
}
Hero( ){ ←ダミー(ここを追加)
…
}
このように同じ名前だが引数が異なるコンストラクタを複数定義することを
オーバーロードという。
この場合、同じ名前のものが複数個定義されていたとしても、
JVM(Javaを動かす仕組み)がnewしてインスタンス化される時に
渡した引数の型や数でどのコンストラクタを動かすかを区別し、動かしてくれる
→複数のコンストラクタが定義されていても、動作するのは1つだけ
ex. public class Main{ //指示クラス
public static void main(String[ ] args){ //指示メソッド
Hero h1=new Hero("太郎");
Hero h2=new Hero( ); ←ダミーで定義した方が呼び出される
JVMはインスタンス化の時に何らかのコンストラクタを必ず実行するので、
全てのクラスには何らかのコンストラクタを定義していなければならない
・・・このルールに従うと、
本来インスタンス化する際に自動で代入して欲しい情報がないクラスでも中身のないコンストラクタ(ダミーのもの)を定義しなけれなならない
ex. public class Main{
public static void main(String[ ] args){
Map( ){
//中身なしのコンストラクタ
}
これは面倒!
そこで、Javaは特例ルールを作った
インスタンスの金型となるクラスに1つもコンストラクタが定義されていない場合に限って
「引数なし、処理内容なし」のコンストラクタ(デフォルトコンストラクタ)の定義がコンパイル時に自動的にクラスへ追加される
※ 1つでもクラスでコンストラクタが定義されていると、このルールは適用除外になるので注意!
ex. class Hero{
String name;
Hero(String name){ ←引数1つのコンストラクタを定義
…
}
}
(Heroをインスタンス化する)
class Main{
public static void main(String[ ] args){
Hero h1=new Hero( );
//引数なし(名前の代入なし)でインスタンス化をする
→特例が適用除外になるので、エラー
この場合だとクラスに引数なしのコンストラクタ Hero ( ){ …}を記述する必要がある
ex. class Hero{
//フィールド
int hp;
String name;
// コンストラクタ
引数1つ
Hero(String name){
this.hp=100;
this.name=name;←受け取った情報(引数)を外部のフィールドに代入
}
引数なし
Hero( ){
this.hp=100;
this.name="ダミー";←情報(引数)を受け取らずに、ここで情報を直接代入
・・・ただこの内部の記述は見方を変えると、
Hero( ){
Hero(ダミー){
this.hp=100;
this.name=name;←"ダミー"を引数として受け取り、代入する
}
という記述に置き換えられる!
・・・ということは
Hero( ){
this.Hero(ダミー);
//ブロックの外側の引数1つのコンストラクタを呼び出す
}
で良いのでは?
・・・ただコンストラクタは自動で実行されるので、
このような置き換えは不可能!(こちらから意図的に指示はできない)
ただこれだと引数の違いだけの似たようなコンストラクタを何度も記述しなければならず、それは効率が悪いので、
Javaでは下記の構文を使えば同一のクラスのコンストラクタを呼び出せるようにした
〈ブロックの外側のコンストラクタを呼び出す構文〉
this(引数)
ちなみに、〈ブロックの外側のフィールドやメソッドを呼び出す構文〉
this.名前
・・・以上を踏まえて書き換えてみると
class Hero{
int hp;
String name;
Hero(String name){
this.hp=100;
this.name=name;
}
Hero( ){
this("ダミー");
}
}
コードがかなりすっきり!