どのクラスもパッケージに属している、ということはいいですね。次はクラスの中身を見てみましょう。前にPerosonというクラスでざっと説明しましたが、ここではそれより少し細かく見てみましょう。
私の長方形
クラスPersonでは、オブジェクトは名前と年齢をデータとして持つことができました。「太郎、5歳」や「次郎、100歳」、覚えてますか?
今度は長方形を表すクラスを考えてみましょう。幅と高さをデータとして持てばよさそうですね。長方形は英語でrectangle、実はjavaの標準ライブラリにRectangleというクラスがあるのです。(パッケージjava.awt)これは座標平面上の長方形のクラスで、幅と高さに加え、位置の情報も持っています。今回作るクラスは、位置の情報は持たない長方形にするので、混同しないよう名前をMyRectangleにします。左が標準ライブラリの中のRectangle、左上の頂点の座標をデータとして持っています。
[ファイルMyRectangle.java]
public class MyRectangle { double width; //インスタンス変数の宣言 double height; //インスタンス変数の宣言 MyRectangle(double w, double h) { //コンストラクタ this.width = w; this.height = h; } double getArea() { //メソッドgetArea return this.width * this.height; } }
クラスMyRectangleは、長方形の製造機になります。出来上がる長方形のオブジェクトは、幅と高さをデータとして持っています。オブジェクトが持つデータをしまっておくところを、インスタンス変数といいます。widthとheightですね。またオブジェクトに対してやらせたい処理を書いたものをメソッドといいます。getAreaがメソッドです。
もうひとつ、コンストラクタがあります。これはオブジェクトを作るとき呼び出すもので、メソッドとは区別します。コンストラクタ名は、クラス名と正確に一致しなければなりません。大文字小文字も気をつけましょう。もしmyRectangleなどとすると、普通のメソッドと解釈されてしまいます。
インスタンス変数とメソッドを一緒にして、このクラスのメンバといいます。コンストラクタはメンバに入らないので注意してください。
インスタンス変数、メソッド、コンストラクタ、クラスのメンバ、これらの用語はしっかり覚えておきましょう。
他にはstaticが頭についた変数やメソッドがありますが、いっぺんに出てくると混乱するので、また後で。
長方形を作ろう
ではクラスMyRectangleのオブジェクトを作り、その長方形の面積を計算してみましょう。
public class Main { public static void main(String[] args) { double s; MyRectangle a; //① a = new MyRectangle(3.0, 5.5); //② s = a.getArea(); //③ System.out.println("Area:" + s); } }
ここで話を単純にするため、どちらのクラスも無名パッケージに属するものとします。
javac Main.java java Main
で
16.5
が出力されます。メソッドの中でもmainは特別で、javaのプログラムの実行はここから始まります。では①から③に対応してどんなことが起こっているのか図にしてみましょう。
①では変数aがクラスMyRectangleのオブジェクトを指すよと宣言しています。実際オブジェクトを生成し、その参照を変数aに代入しているのは②です。ここではコンストラクタを呼び出しています。
コンストラクタの変数wとhは仮引数(かりひきすう)といいます。くれぐれも「かりいんすう」と読まないように。仮引数はこのコンストラクタ内でのみ有効です。つまりコンストラクタ内の処理が終わると、消えてなくなってしまいます。呼び出しでカッコ内に指定された3.0と5.5が、順にwとhに入ります。ここでは幅と高さを渡しているので、インスタンス変数のwidthとheightに代入してやればよいわけです。こういうときは、インスタンス変数の名前の前に「this」をつけて呼びましょう。(実は省略できるのですが、最初のうちは仮引数などと区別するためにつける習慣にしてみましょう。)
最後に③で長方形aの面積を求めます。aのメソッドgetAreaを呼び出すには、aの後ろに、ピリオドに続いてメソッド名を指定します。変数sに結果の面積が代入されます。
s = a.getArea();
呼び出されたメソッドgetAreaではオブジェクトaのインスタンス変数widthとheightの値を掛けたものを求めればいいわけです。メソッド内でのインスタンス変数にも、コンストラクタと同様、「this」を頭に付けます。(省略可能)return文でこれらを掛けた値を、呼び出し元に返します。この値を戻り値といいます。メソッドの先頭のdoubleは戻り値の型です。
メソッド getArea
double getArea() { return this.width * this.height; ←return文の式の値が戻り値になる }
このメソッドがなくても、クラスMainでは
s = a.width * a.height;
で面積を求めることができます。でもオブジェクトが自分自身のやるべきことの詳細を知っていることが重要なのです。長方形の面積くらいならいいですけれど、もっと複雑な図形になったらどうでしょう。よく子供に言ってしまうのですが、「自分でできることは自分でやって頂戴!」です。でもできないことは無理ですよね。上は面積を求めるメソッドを持った賢い長方形、下は持たない長方形です。
長方形はいくつ?
さあ問題です。長方形はいくつできるでしょうか?
public class Main1 { public static void main(String[] args) { MyRectangle a, b, c, d; a = new MyRectangle(3.0, 5.5); //① b = new MyRectangle(3.0, 5.5); //② b.width = 5.0; //③ System.out.println("a.width=" + a.width); c = new MyRectangle(4.0, 6.5); //④ d = c; //⑤ d.width = 5.0; //⑥ System.out.println("c.width=" + c.width); } }
正解は三つです。下の図で説明しましょう。
①と②で、同じ大きさの長方形を作っています。でも単に同じ大きさというだけで、別の長方形が二つ出来上がります。
さて問題は⑤です。変数cには、4.0×6.5の長方形のオブジェクトの参照(どこにあるかという情報)が入っています。これを変数dに代入することで、dも同じオブジェクトを参照することになるのです。⑥でd.widthを変更するということはつまり、c.widthを変更していることになるので、出力は5.0になります。
もっと賢く
長方形をもっと賢くしましょう。自分で大きさを変えられるsetSize、正方形かどうかを判断するisSquareという二つのメソッドを追加してみます。
public class MyRectangle { double width; double height; MyRectangle(double w, double h) { this.width = w; this.height = h; } double getArea() { return this.width * this.height; } void setSize(double w, double h) { this.width = w; this.height = h; } boolean isSquare() { return this.width == this.height; } }
次でtrueが出力されます。
public class Main { public static void main(String[] args) { MyRectangle a = new MyRectangle(3.0, 4.0); a.setSize(5.5, 5.5); System.out.println(a.isSquare()); } }
ここで目新しいのは、メソッドsetSizeの呼び出しです。カッコ内に新しい幅と高さを指定します。ここに指定したものを実引数(じつひきすう)といいます。実引数と仮引数は先頭から1対1に対応します。また戻り値は必要ないので、メソッドsetSize側での戻り値の型を指定するところはvoidとします。
a.setSize(5.5, 5.5); ↓ ↓ void setSize(double w, double h) { this.width = w; this.height = h; }
コンストラクタの呼び出しで、3.0と4.0がカッコ内にありますが、これも実引数です。
もう一点、メソッドsetSizeの中身はコンストラクタと同じなのですが、この二つを混同しないでください。あくまでsetSizeはインスタンス変数の値を変更するだけで、オブジェクトの生成は行いません。
次の章からも、このMyRectangleを使っていろいろ解説していきます。そのつど都合で必要なメソッドのみ出しますが、説明のためなので混乱しないように。
コンストラクタがないのに...
さて第2章で出てきたクラスPersonは、コンストラクタがありませんでした。それなのに
x = new Person();
のようにコンストラクタの呼び出しをしていましたね。実は、クラスの中に他にコンストラクタがない場合、特別なコンストラクタが自動的に作られるのです。といってもコンパイラが作るので、ソースはなんら変化はありません。これをデフォルトコンストラクタといいます。デフォルトコンストラクタは引数なしの何もしないコンストラクタです。2章のクラスPersonはpublicだったので、作られるデフォルトコンストラクタもpublicになります。
public Person() { super(); }
super();は、この後継承を勉強するとわかりますが、ここではとりあえず、何もしないと考えておいてください。
もしクラス本体のアクセス指定が引っ込み思案、つまりclassの前に何も付いていない場合は、デフォルトコンストラクタも何も付かない次の形になります。これは同一パッケージ内からのみアクセス可能でしたね。
Person() { super(); }