第 5 回オブジェクト指向プログラミング (1)

今回の内容

今回は複素数の計算を行うプログラムを題材として、オブジェクト指向プログラミングについて学びます。

オブジェクト指向プログラミング概観

ふたつの複素数の加算を行うメソッドを作ってくださいと現段階で言われたとすると、 あなたは以下のようなソースを書こうとして困ると思います。

static double add(double re1, double im1, double re2, double im2){
  double re, im;
  re = re1 + re2;
  im = im1 + im2;

  return re; // 困った。double の値をひとつしか返せない。
}

複素数はそれ自身ひとつの数であるのに、 プログラムではふたつの double を使わないと表せません。 つまり、 double 型変数をふたつもった一塊の型が新しく必要になります。 オブジェクト指向プログラミングでは、 複数のデータとそれを扱うメソッドを一塊にした型を作ります。

(C 言語を既習の人向け) C 言語では、そういうときは構造体を使うとうまくいきました。 構造体に自分のデータを扱う関数をまとめたものがオブジェクトです。

例えば、複素数を表す型は以下に挙げる性質を持ちます。

このようにそのものに一般的な性質を記述したものを クラス といい、 その性質をもった個々のものを オブジェクト といいます。 あるクラスに属す変数やメソッドをそのクラスの メンバ といいます。 クラスの定義の仕方は以下のようになります。

class クラス名{
  // メンバ変数とメンバメソッドを定義する
}

例えば、複素数のクラスは以下のように記述できます。

class complex{
  double re, im; // メンバ変数

  public static add(complex a, complex b){
    // 中身は略
  }

  // 他のメソッド
}

複素数クラスから複素数のオブジェクトを作るには、以下のようにします。

complex a = new complex();

複素数を表すクラス

 
01 public class complex{
02   private double re,im; // メンバ変数
03
04   public complex(double x, double y){ // コンストラクタ
05     re = x;
06     im = y;
07   }
08
09   public static complex plus(complex x , complex y){
10     return new complex(x.re + y.re, x.im + y.im);
11   }
12
13   public static complex multiply(complex x , complex y){
14     return new complex(x.re*y.re - x.im*y.im, x.re*y.im + x.im*y.re);
15   }
16
17   public String toString(){ // a + bi という表記に整えるメソッド
18     String s = "";
19     if(re != 0)
20       s += re;
21     if(im >0)
22       s += "+" + im + "i";
23     else if(im < 0)
24       s += im + "i";
25     return s;
26   }
27 }

上のクラスを用いたサンプルプログラム

class sample5a{
  public static void main(String[] args){
    complex a,b,c;
    a = new complex(1.0,1.0);
    b = new complex(-1.0,1.0);
	
    c = complex.plus(a,b);
    System.out.println(a.toString() + " + " + b.toString() + " = " +  c.toString());

    c = complex.multiply(a,b);
    System.out.println(a.toString() + " * " + b.toString() + " = " +  c.toString());
  }
}

実行

g1*****@ux001>javac complex.java
g1*****@ux001>javac sample5a.java
g1*****@ux001>java sample5a
1.0 + 1.0i + -1.0 + 1.0i = 2.0i
1.0 + 1.0i * -1.0 + 1.0i = 2.0

今回は、 sample5a が以下のように (なんとなく) わかれば結構です。

  1. 複素数 a, b, c があります。
  2. a に 1+1i を、bに -1+1i を代入
  3. c に a と b の和を代入して表示
  4. c に今度は a と b の積を代入して表示

複素数というクラスを作ることによって、 それを使うときには加算や乗算の中身などを気にしなくてよくなりました。 オブジェクト指向プログラミングをうまく使うと、 このようにプログラムの部品を作ることができます。

static

static を前につけると、 static なメンバが定義できます (例: 9 行目の add)。

static なメンバ はクラスごとにあるメンバで、 static ではないメンバはオブジェクトごとにあります。 以下に図解します。

// re, im はオブジェクト a, b, c それぞれにある (static ではない)
// add, multiply はクラス complex にひとつだけ (static)
complex (add, multiply) -+- a (re, im)
                         |
                         +- b (re, im)
                         |
                         +- c (re, im)

特別なメソッド

コンストラクタ
コンストラクタはクラスと同名のメソッドで、そのクラスのオブジェクトを作るときに実行されます。 オブジェクトが作られるときに呼び出されるので、メンバ変数の初期化に用いられます。 コンストラクタを呼び出すときは、その前に new 演算子が必要です (例: sample5a)。 「コンストラクタによって作成されたオブジェクト」への参照 (次章) が戻ります。 コンストラクタを定義するときは戻り値の型を書きません。
main
main はそのクラスを java で実行したときに最初に呼ばれるメソッドです。 static なメソッドとして定義します。

参照

参照 はオブジェクトの名前です。 コンストラクタを呼び出すと参照が戻り値として得られます。 あるオブジェクトのメンバを用いるときは、ドット演算子 . を使って、 参照.メンバ とします。 static なメンバを用いるときも同様にドット演算子 . を使って、 クラス名.メンバ とします。

参照を入れる変数を作るときは、 クラス名 変数名; とします。 参照を引数とするメソッドを定義するときも同様です。

まとめ

オブジェクト指向プログラミングについて概観しました。 余力のある人は練習として、複素数の他の演算を定義してください。