コンストラクタの記述

オブジェクトを作るには,「new クラス名()」とすれば良いが,オブジェク トを作る際に,要素の初期値を設定したりなど付随する操作をおこないたいこ とがある.これまでの例では,Point2D型のオブジェクトを作るたびに,イン スタンス変数 x, y を設定していた.これを初期化のためのメソッド initialize を作って実現してみると,
class Point2D {
  double x,y;
    // Point2Dのインスタンス変数を初期化するためのインスタンスメソッド
  void initialize(double xx,double yy){
    this.x=xx; this.y=yy;
  }
  double length(){
    return Math.sqrt(x*x+y*y);
  }
  double distance2D(Point2D point2){
    double dx=(x-point2.x),dy=(y-point2.y);
    return Math.sqrt(dx*dx+dy*dy);
  }
}

class ObjectTest5 {
  public static void main(String[] args){
    Point2D p1,p2,p3;
      // Point2Dクラスのインスタンスを作成する
    p1=new Point2D();
      // インスタンス変数の中身を初期化する。
    p1.initialize(0.0,0.0);
    p2=new Point2D();
    p2.initialize(1.0,1.0);
    p3=new Point2D();
    p3.initialize(0.0,1.0);
    System.out.println("length of p1 = "+p1.length());
    System.out.println("length of p2 = "+p2.length());
    System.out.println("length of p3 = "+p3.length());
    System.out.println("distance between p1 and p2 "+p1.distance2D(p2));
    System.out.println("distance between p2 and p3 "+p2.distance2D(p3));
    System.out.println("distance between p3 and p1 "+p3.distance2D(p1));
  }
}
となる.このようにオブジェクトの作成と初期化のためのメソッド呼び出しは, 連携しておこなわれることが多い.

そのため,クラスの定義中にコンストラクタ(constructor,構築子と訳される こともある)と呼ばれる特別なメソッドを作っておいて,「new クラス名(引数 1, .. , 引数n)」としてオブジェクトを作ると引数の型が合うコンストラクタ が呼ばれるようにできる.コンストラクタは通常のメソッドのように,返り値 を持たない(void 型の返り値を持つとも言えるが,void とは書かない)

コンストラクタを使って,書き直したのが以下のプログラムである(コンスト ラクタの中での「this.」は省略して書いた.省略しないで書いても問題ない).

class Point2D {
  double x,y;
    // Point2Dクラスのコンストラクタ((double,double)型の引数を持つ場合)の宣言
  Point2D(double xx,double yy){
    x=xx; y=yy;
  }
  double length(){
    return Math.sqrt(x*x+y*y);
  }
  double distance2D(Point2D point2){
    double dx=(x-point2.x),dy=(y-point2.y);
    return Math.sqrt(dx*dx+dy*dy);
  }
}

class ObjectTest5 {
  public static void main(String[] args){
    Point2D p1,p2,p3;
      // Point2Dクラスのインスタンスをコンストラクタを呼び出して作成する。
    p1=new Point2D(0.0,0.0);
    p2=new Point2D(1.0,1.0);
    p3=new Point2D(0.0,1.0);
    System.out.println("length of p1 = "+p1.length());
    System.out.println("length of p2 = "+p2.length());
    System.out.println("length of p3 = "+p3.length());
    System.out.println("distance between p1 and p2 "+p1.distance2D(p2));
    System.out.println("distance between p2 and p3 "+p2.distance2D(p3));
    System.out.println("distance between p3 and p1 "+p3.distance2D(p1));
  }
}
オブジェクトが作られる際に呼ばれるコンストラクタ(constructor)に対応す るものとして,オブジェクトが不要になって捨てられる際に呼ばれるファイナ ライザ(finalizer,C++などではデストラクタ(destructor)と言われるもの) があるが,それほど出てこない.


Java言語ではオブジェクトは基本的には「new クラス名(引数)」というコンス トラクタの呼び出しによってのみ作られる。例外としては、文字列定数や配列 の初期化がある。
class T{
  public static void main(String[] args){
    int table[]={1,2,3,1,2};
//              ^^^^^^^^^^^ これが配列の初期化          
    System.out.println("Hello World");
//                     ^^^^^^^^^^^^^ これが文字列定数
  }
}
 オブジェクトはヒープ領域というところに置かれる。変数はローカル変数(メ ソッド内の変数)、インスタンス変数、クラス変数等によって置かれる場所が違 うが、オブジェクト型の変数はオブジェクトへの参照だけを持つ小さい(1ワー ドの)箱となっている。

例として、

class Point{
  int x,y;
  Point(int xx, int yy){ x=xx; y=yy; }
}
で、
Point p1=new Point(1,2);
とした時の絵を下に示す。
代入や引数渡しをする際はオブジェクトを新たに作って渡すのではなく、オブジェクトへの 参照をそのまま渡す。従って、下のように p1をp2に代入してからp2.xを書き換えると、 p1.xも書き変わっている。
// オブジェクトの生成
Point p1=new Point(1,2);
// 代入ではオブジェクトを新たに生成せず
// 参照のみコピーしたので、p2とp1は同じところを指すようになる
Point p2=p1;
// p2を書き換え
p2.x=3;
// 1ではなく3が表示される
System.out.println(p1.x);
引数渡しをする場合も同様である。
class T1{
  // pのxを返すがついでにp.xを1増やす
  static int getXandIncrement(Point p){
    int x=p.x;
    p.x++;
    return x;
  }
  public static void main(String args[]){
    Point p1=new Point (1,3);
    // 1を表示する
    System.out.println(getXandIncrement(p1));
    // 2を表示する
    System.out.println(p1.x);
  }
}
このことを考えると、演算子 == がオブジェクトの同一性を表すという意味が分かってく るかもしれない。
class Point {
    int x,y;
    Point(int xx,int yy){
	this.x=xx; this.y=yy;
    }
    //    public boolean equals(Point p){
    //	return x==p.x && y==p.y;
    //    }
}
class t{
    public static void main(String[] args){
	Point p1=new Point(0,1);
	Point p2=new Point(0,1);
        // == は同一性を表すので falseになる
	System.out.println(p1==p2);
        // == は同一性を表すので trueになる
	System.out.println(p1==p1);
        // == は同一性を表すので trueになる
	System.out.println(p2==p2);
        // Point クラスで equalsが定義していない時は == と同じ
	System.out.println(p1.equals(p1));
        // equalsは同値性を表すように定義するので true にしたいが、
	System.out.println(p1.equals(p2));
    }
}
上の例でPointのequalsは未定義だが、Java言語のすべてのクラスは java.lang.Object という名前のクラスのサブクラスとなっていて、その中で equalsは
class Object {
  ....
  public boolean equals(Object o){
    return this==o;
  }
}
のように定義されているため、そのようになっている。
ここで、述べたオブジェクトと参照の関係は多くのオブジェクト指向言語でも 通用する議論だが、世の中でよく使われる C++ という言語では成り立たない。 C++では代入やメソッドへの引数渡しなどいたるところで、オブジェクトが作ら れる。

C++では演算子のオーバーライドという機能が用意されていて、この機能を利用して 演算子 == は同一性を表すのではなく、同値性を表すように定義されているのが普 通である。


次へ進む