前々回の,メソッドとアクセサメソッドを導入した弾むだけのボールのプログラムを示す.
Ball ball0; final float GRAVITY = 9.8 / 60; final float ELAS = 0.98; void setup(){ size(500, 500); ball0 = new Ball(); ball0.setX(width / 2); ball0.setY(height / 2); ball0.setVX(10); ball0.setVY(-10); ball0.setD(50); } void draw(){ background(255, 255, 255); ball0.move(); ball0.draw(); } class Ball{ float x; float y; float vx; float vy; float d; void setX(float x){ this.x = x; } void setY(float y){ this.y = y; } void setVX(float vx){ this.vx = vx; } void setVY(float vy){ this.vy = vy; } void setD(float d){ this.d = d; } void move(){ this.x += this.vx; this.vy += GRAVITY; this.y += this.vy; if (this.x < (this.d / 2)){ this.x = this.d / 2; this.vx = ELAS * -this.vx; } if (this.x > (width - (this.d / 2))){ this.x = width - (this.d / 2); this.vx = ELAS * -this.vx; } if (this.y > height - (this.d / 2)){ this.y = height - (this.d / 2); this.vy = ELAS * -this.vy; } } void draw(){ ellipse(this.x, this.y, this.d, this.d); } }
今回注目するのは,以下のsetup関数内のインスタンスを作っている箇所である.
ball0 = new Ball(); ball0.setX(width / 2); ball0.setY(height / 2); ball0.setVX(10); ball0.setVY(-10); ball0.setD(50);
インスタンスを作った後,各種属性を初期化するためアクセサメソッドを利用している.しかし,各種属性を一つずつ初期化しており,長ったらしい.
そこで,コンストラクタを導入することで以下のようにすっきりとできる.
ball0 = new Ball(width / 2, height / 2, 10, -10, 50);
要するにnew演算子でインスタンスを作ると同時に,各種属性を初期化(指定)できるようにしてしまおう,というわけである.
また,アクセサメソッド(set???)は初期化にしか使用していなかったので不要である.よって,これらアクセサメソッドも削除できる.
コンストラクタを導入し,書き直したプログラムは以下になる.
Ball ball0; final float GRAVITY = 9.8 / 60; final float ELAS = 0.98; void setup(){ size(500, 500); ball0 = new Ball(width / 2, height / 2, 10, -10, 50); } void draw(){ background(255, 255, 255); ball0.move(); ball0.draw(); } class Ball{ float x; float y; float vx; float vy; float d; Ball(float x, float y, float vx, float vy, float d){ this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.d = d; } void move(){ this.x += this.vx; this.vy += GRAVITY; this.y += this.vy; if (this.x < (this.d / 2)){ this.x = this.d / 2; this.vx = ELAS * -this.vx; } if (this.x > (width - (this.d / 2))){ this.x = width - (this.d / 2); this.vx = ELAS * -this.vx; } if (this.y > height - (this.d / 2)){ this.y = height - (this.d / 2); this.vy = ELAS * -this.vy; } } void draw(){ ellipse(this.x, this.y, this.d, this.d); } }
コンストラクタは,newによりインスタンスが作られた際に呼ばれる特殊なメソッドである.
上記のプログラムでは以下の箇所となる.
コンストラクタを作る際には,メソッドの戻り値は記述せず(voidも書かない),メソッド名をクラス名と同じにすることでコンストラクタとなる.
Ball(float x, float y, float vx, float vy, float d){ this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.d = d; }
また,上記のメソッドを作った時点で,
ball0 = new Ball();
と書くことはできない.引数のないコンストラクタが存在しないためである.
「いままでコンストラクタを一切書いてこなかったけどnew Ball();でインスタンスを作れていたじゃないか.」と思われるかもしれないが,実は,今まで書いてはいなかったが自動的にコンストラクタが作られていた.
私たちの目には見えていなかったが,パソコンが勝手に以下のようにプログラムを書き換えていた,と思ってもらってもよい.
class Ball{ float x; float y; float vx; float vy; float d; Ball(){ //クラスにコンストラクタが一つもない場合は //引数がなく,何も行わない } //コンストラクタが内部で勝手に作られる void move(){ this.x += this.vx; this.vy += GRAVITY; this.y += this.vy; if (this.x < (this.d / 2)){ this.x = this.d / 2; this.vx = ELAS * -this.vx; } if (this.x > (width - (this.d / 2))){ this.x = width - (this.d / 2); this.vx = ELAS * -this.vx; } if (this.y > height - (this.d / 2)){ this.y = height - (this.d / 2); this.vy = ELAS * -this.vy; } } void draw(){ ellipse(this.x, this.y, this.d, this.d); } }
上記のように,引数がなく,何も行わない,勝手に作成されるコンストラクタを「暗示的コンストラクタ」と呼ぶ.
クラス内に,コンストラクタが一つもない場合は,この暗示的コンストラクが内部で自動的に作られる.
そのため,いままで
ball0 = new Ball();
と書くことができた.
もし,
void setup(){ size(500, 500); ball0 = new Ball(); ball1 = new Ball(width / 2, height / 2, 10, -10, 50); }
という風に,異なるコンストラクタを同時に使いたい場合には,Ballクラスに複数のコンストラクタを作ればよい.
class Ball{ float x; float y; float vx; float vy; float d; Ball(){ } Ball(float x, float y, float vx, float vy, float d){ this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.d = d; } void move(){ this.x += this.vx; this.vy += GRAVITY; this.y += this.vy; if (this.x < (this.d / 2)){ this.x = this.d / 2; this.vx = ELAS * -this.vx; } if (this.x > (width - (this.d / 2))){ this.x = width - (this.d / 2); this.vx = ELAS * -this.vx; } if (this.y > height - (this.d / 2)){ this.y = height - (this.d / 2); this.vy = ELAS * -this.vy; } } void draw(){ ellipse(this.x, this.y, this.d, this.d); } }
となる.こうしておけば,
ball0 = new Ball();
では引数がないコンストラクタ,
Ball(){ }
が自動的に使われるし,
ball1 = new Ball(width / 2, height / 2, 10, -10, 50);
では,引数があるコンストラクタ
Ball(float x, float y, float vx, float vy, float d){ this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.d = d; }
が自動的に使われる.
コンストラクタに限らず,同名のメソッドが存在する場合,メソッド呼び出しもとの,引数の数,型の種類(これをシグネチャと呼ぶ)から,対応するメソッドが自動的に使われる仕組みがクラスには存在する.
このように,同名だが,シグネチャの違いで数種類のメソッドを作ることをメソッドのオーバーロードと呼ぶ.