コンストラクタ

Revised: Feb./23rd/2003: Since: Dec./29th/2001

クラスをある程度作成できるようになりました。ここで、インスタンス化についてもう一度考えてみましょう。

インスタンス化とコンストラクタ

インスタンス化の基本書式は次のようになります:

クラス名 インスタンス変数名;
インスタンス変数 = new クラス名();

ここで、二行目では、コンストラクタと呼ばれる特殊なメソッドの呼びだしという意味があります。コンストラクタは、クラスをインスタンス化してオブジェクトを作成するときに、必ず実行されています。オブジェクトの初期化という重要な役割を果たします。コンストラクタは、クラス名と同じ名前を持ち、戻り値を持たない特殊なメソッドです。

従って、上記の基本書式は、正確には次のような意味があります:

クラス名 インスタンス変数名;
インスタンス変数 = new コンストラクタ名();

又は、

クラス名 インスタンス変数名 = new コンストラクタ名();

コンストラクタによって、クラスのインスタンスがメモリ上に作られ、それを参照するためのIDが new 演算子によって左辺の変数に代入されていることになります。

コンストラクタとはなんだろう

コンストラクタは特殊なメソッドです。クラス名と同じ名前で、 void キーワードを持たず戻り値宣言もないメソッドです。引数の異なるコンストラクタを同時に定義することも可能です。これをオーバーロードと呼びます。

今までクラスの作成時に、コンストラクタを作ってきませんでした。コンストラクタが全く記述されていないクラスに限り、 Java コンパイラ javac は、自動的に、引数なしで内容が空のデフォルト・コンストラクタを、生成するバイトコード内に埋め込んでくれます。引数をもつコンストラクタを自分で作ると、 javac は引数の無いコンストラクタをもう作ってはくれないので、自分で実装する必要があります。

働きとしては、オブジェクトの初期化です。インスタンス化時に必ず呼ばれるので、必要な初期化ロジックを実装できます。メンバ変数の説明のときに、メンバ変数は暗示的に初期値が代入されると言いましたが、普通はコンストラクタを明示的に作成して、インスタンス化のときに、コンストラクタに受け渡す引数を通して初期化します。

デフォルト・コンストラクタの勧め

コンストラクタを明示的に作らないと、コンパイラが自動的に引数がなく内容が空のコンストラクタを作ってくれます。一つでも引数を持つコンストラクタを作ると、コンパイラは自動的に作ってくれなくなります。

コンストラクタを明示的に記述するときは、デフォルトとなる引数のないコンストラクタも用意しておいたほうが良いでしょう。つまり、引数を持たず、メンバー変数にデフォルト値を代入数するコンストラクタを作っておくべきです。これは、そのクラスのユーザが、簡単にデフォルトのオブジェクトを作れるようにしておくということと、後で説明する継承時に必要であるという二つの理由が挙げられます。

例えば、メンバー変数 String name を持つクラス Dog があって、コンストラクタ Dog(String aName) を記述する場合、 Dog() も用意しておくことになります。

class Dog {
	private String name;
	Dog() {
		name = "Anonymonus";
	}
	Dog(String aName) {
		name = aName;
	}
	public String getName() {
		return name;
	}
}

コンストラクタの実装

コンストラクタと言っても、基本的にはメソッドと変わるところはありません。簡単な例で利用方法を確認しておきます。

TestConstructor.java:

class Const {
	//メンバ変数
	double x;
	int y;
	//コンストラクタ
	Const(double x, int y) {
		this.x = x;
		this.y = y;
	}
	//コンストラクタのオーバーロード
	Const(double x) {
		this.x = x;
		this.y = 15;
	}
	//コンストラクタのオーバーロード
	Const() {
		this.x = 10;
		this.y = 15;
	}
	//メソッド
	double getMean() {
		double z = this.x/this.y;
		return z;
	}
}
class TestConstructor {
	public static void main(String args[]) {
		double m1,m2;
		//インスタンス変数宣言
		Const obj1;
		Const obj2;
		//コンストラクタ呼び出し(インスタンス化)
		obj1 = new Const(1059.0, 12);
		obj2 = new Const(1298.0);
		//メソッド呼び出し
		m1 = obj1.getMean();
		m2 = obj2.getMean();
		System.out.println("obj1: " + m1);
		System.out.println("obj2: " + m2);
	}
}

このサンプルは二つのクラスからなっています。 Const クラスではコンストラクタが二つ定義されています。

インスタンスを作成する側のメソッドである main() から、それぞれのコンストラクタが呼ばれていますが、引数の個数に応じて、対応するコンストラクタへ制御が移ります。ここでは、引数のデータ型も判定されます。

obj1 の作成では、引数が二つあるコンストラクタが呼ばれていますから、一つ目のコンストラクタに制御が移り、メンバ変数 xy に値が代入されます。ここで、 this キーワードが付いているものは、その変数が実行中のオブジェクトのメンバーである変数(インスタンス変数)であることを明示しています。

obj2 の作成では、引数が一つだけのコンストラクタが呼ばれています。対応するコンストラクタは二つ目に見付かり、データ型も適切です。ここに制御が移り、メンバ変数が初期化されます。

C:\Java>javac TestConstructor.java
C:\Java>java TestConstructor
obj1: 88.25
obj2: 86.53333333333333

予想どうりの結果だったでしょうか?まぎれがあるようでしたら、次の二点に注目して、ソースを確認してください:

ここではコンストラクタを二つ作り、それぞれ別のインスタンス化に利用しました。一つのコンストラクタから複数のインスタンスを作ることも出来ます。その方が普通でしょう。

コンストラクタのメリット

コンストラクタでメンバ変数を初期化することは、インスタンス作成時にインスタンスの変数の値を指定できると言うことです。つまり、インスタンス化するオブジェクトごとに固有の初期値を持たせることが出来るわけです。クラスを利用する他のクラスから見れば、専用の初期化メソッドをわざわざ呼び出す必要がなくなります。

コンストラクタを複数作ることは、コンストラクタのオーバーロードです。コンストラクタもメソッドの一つと考えれば、メソッドのオーバーロードで挙げたメリットは全て当てはまります。



Copyright © 2001, 2003 SUGAI, Manabu. All Rights Reserved.
SEO [PR] [ Windows7 f ^T[o[ Cu`bg SEO