連載
» 2003年05月08日 00時00分 UPDATE

いまから始めるJava(5):メソッドとコンストラクタはなぜ必要?

[平井玄,@IT]

 今回はメソッドとコンストラクタについて説明します。そしてその説明を通して、プログラムには制御構造というものが必要であることを理解しましょう。

 前回まで、Javaの変数やクラスについて、一般的な例え話ではなく、どのようにメモリを使うのかという視点で説明してきました。今回はコンピュータの中で最も重要な部品であるCPUが、プログラムをどのように実行するのか、という視点から入りましょう。

 まず、CPUはどのようにプログラムを実行するのでしょうか? 画面に文字を表示する次のプログラムの実行結果を、ソースコードを見て想像してみてください。

   画面に文字を表示するプログラム
1: public class WriteLines {
2:  public static void main( String args[] ) {
3:    String s1 = "1行目";
4:    String s2 = "2行目";
5:
6:    System.out.println(s1);
7:    System.out.println(s2);
8:  }
9: }

 実行結果をこんなふうに考えたら正解です(1〜2行目の意味はまだ説明していない要素が含まれますので、省略します)。

  • 3行目ではString型の変数s1に「1行目」という文字列を代入している
  • 4行目ではString型の変数s2に「2行目」という文字列を代入している
  • 6行目ではString型の変数s1、つまり「1行目」という文字列を画面に出力している
  • 7行目ではString型の変数s2、つまり「2行目」という文字列を画面に出力している
  • プログラムの最後に達したので処理を終了する

 では、実際にどうなるか実行してみましょう。

C:\DOCUME~1\MYDOCU~1\MYJAVA~1>javac WriteLines.java

C:\DOCUME~1\MYDOCU~1\MYJAVA~1>java WriteLines
1行目
2行目


C:\DOCUME~1\MYDOCU~1\MYJAVA~1>

 確かに予想どおりの結果になりました。「当たり前だ!」と思うかもしれませんが、どうして当たり前に思えるのか、というところにCPUとプログラムの関係を理解する手掛かりがあります。

 私たちがこのプログラムの実行結果を簡単に予想できるのは、私たち人間がプログラムを読むのと同じ順番でCPUもプログラムを実行しているからです。これは偶然の一致ではなく、私たちが普段使っているコンピュータの原理を考えた人が人間をモデルにして「コンピュータ」という概念を考え、それに基づいてCPUが作られたからです。CPUがプログラムを実行する様子は、ちょうど次のような紙テープを読んでいくのと同じです。

          …………,+, 1, 1,×,2,3,……………


 CPUはまず「+」という記号を読んで「足し算モード」になります。足し算には「足される数」と「足す数」の2つが必要ですので、2番目と3番目の文字を読んで「1+1」を計算します。次の文字は「×」ですのでCPU内部は「掛け算モード」になります。掛け算には「掛けられる数」と「掛ける数」の2つが必要ですので、5番目と6番目の文字を読んで「2×3」を計算します。

 CPUはこうした紙テープを読んでいくのと同じように、メモリから実行コードを次々に読み込んで実行する装置です。第1回「Java2 SDKで学習の準備」で説明したように、Javaのプログラムはコンパイラによって中間コードに置き換えられ、Javaの仮想マシンによって実行コードに解釈されて実行されます。Javaでプログラムを書いても、最終的には実行コードとしてCPUが実行するわけです。

プログラムを省略できる「メソッド」

 第4回「クラスの継承の本質を知る」では地図上のある地点の緯度、経度、高度を表すGeographicInfo3D型のインスタンスを作り、メンバ変数の内容を次のように表示しました。

public class UsingGeographicInfo3D {
  public static void main( String args[] ) {
    GeographicInfo3D g = new GeographicInfo3D();
    g.latitude = 35.66;
    g.longitude = 139.75;
    g.height = 10.0;
    

  System.out.println(g.latitude);
    System.out.println(g.longitude);
    System.out.println(g.height);
  }
}

 では、GeographicInfo3D型の変数hを宣言してインスタンス化し、さらにgとhの内容を表示するにはどうしたらよいでしょうか。最も単純な方法は、以下のようにhのメンバ変数を表示させるコードを追加することです。

public class UsingGeographicInfo3D {
  public static void main( String args[] ) {
    GeographicInfo3D g = new GeographicInfo3D();
    g.latitude = 35.66;
    g.longitude = 139.75;
    g.height = 10.0;
    GeographicInfo3D h = new GeographicInfo3D();
    h.latitude = 38.25;
    h.longitude = 137.65;
    h.height = 12.0;


    System.out.println(g.latitude);
    System.out.println(g.longitude);
    System.out.println(g.height);


    System.out.println(h.latitude);
    System.out.println(h.longitude);
    System.out.println(h.height);

  }
}

 プログラムは順番に実行されるものですから、これでも間違いではありません。しかし、GeographicInfo3D型の変数g、hのメンバ変数を表示させるのに、ほとんど同じコードを書いているのは、無駄が多いように思えます。そもそも、2つの処理で異なるのはgとhという処理の対象であって、メンバ変数を生成、表示するという処理そのものはまったく同じです。

 このようなとき、Javaでは「メソッド」(method:方法)という機能を使います。メソッドを使うと、処理の対象だけが異なる場合に似たようなコードを書かなくて済むようになります。そこで、GeographicInfo3Dクラスにメンバ変数を表示するというメソッドを追加してみましょう。

   メソッドを使うプログラム
class GeographicInfo {
  double latitude;
  double longitude;
}


class GeographicInfo3D extends GeographicInfo {
  double height;


  void display() {
    System.out.println(latitude);
    System.out.println(longitude);
    System.out.println(height);
  }

}


public class RunClassWithMethod {
  public static void main( String args[] ) {
    GeographicInfo3D g = new GeographicInfo3D();
    g.latitude = 35.66;
    g.longitude = 139.75;
    g.height = 10.0;
    GeographicInfo3D h = new GeographicInfo3D();
    h.latitude = 38.25;
    h.longitude = 137.65;
    h.height = 12.0;


    g.display();
    h.display();

  }
}

 赤字の部分が変更された個所です。「void」のような語句は次回で説明します。いまは、プログラムの後半にあった画面に文字を表示させる部分がGeographicInfo3Dクラスの宣言内に移動していることに注目してください。

 さて、変更された個所には元の文とは若干異なる個所があります。元の文では以下のように、出力の対象はあくまでも「gという変数のメンバであるlatitude」だったり、「hという変数のメンバであるlatitude」です。

System.out.println(g.latitude);

 ところが変更後のプログラムでは以下のように、出力の対象が「latitudeという変数」になっています。

System.out.println(latitude);

 このことはプログラム後半の以下の部分とも関係しています。

g.display();

 変数と違って、メソッドには必ず「display()」のように括弧が付きます。また、「g.latitude」のように「インスタンス名.変数名」とすると、「あるインスタンスに所属するメンバ変数」という意味になりますが、「g.display()」のように「インスタンス名.メソッド名」とすると、「あるインスタンスに対するメソッド」という意味になります。

 ここで、クラスのメンバ変数がメモリ内でどのように管理されるのかも思い出してください。メンバ変数はクラス型変数に格納された先頭アドレスからの相対距離でアクセスされるのでした。一方、このプログラムを実行すると、「public static void main( String args[] )」で始まるブロックから処理が始まり、変数g、hが宣言され、初期化された後で「g.display()」と「h.display()」が実行されます。

 ただし、display()というメソッド内での表示対象はlatitude、longitude、heightとなっていて、これらがどのインスタンスに所属するのかは定義されていません。メソッドを実行したときにどのインスタンスのメンバ変数が表示されるのかは、メソッドを呼び出したときのインスタンスが何であったかによって決まるのです。

「コンストラクタ」はクラスと同じ名前を持つメソッド

 メソッドを使うとプログラムを省略できることが分かりました。しかし、まだ冗長なところが残っています。メンバ変数の初期化も似たような処理なので、メソッドを使って書き換えてしまいましょう。

 Javaにはメンバ変数の初期化など、クラスをインスタンス化したときの定型処理をクラスの機能として実行してくれる特別なメソッドとして「コンストラクタ」(constructor:建設者)という機能が用意されています。コンストラクタは、クラス名と同じ名前を持つメソッドのことで、インスタンス化したときに一度だけ実行されます。

   メソッドを使うプログラム
class GeographicInfo {
  double latitude;
  double longitude;
}


class GeographicInfo3D extends GeographicInfo {
  double height;


  GeographicInfo3D( double la, double lo, double he ) {
    latitude = la;
    longitude = lo;
    height = he;
  }


  void display() {
    System.out.println(latitude);
    System.out.println(longitude);
    System.out.println(height);
  }
}


public class RunClassWithConstructor {
  public static void main( String args[] ) {
    GeographicInfo3D g = new GeographicInfo3D( 33.66, 139.75, 10.0);
    GeographicInfo3D h = new GeographicInfo3D( 38.25, 137.65, 12.0 );


    g.display();
    h.display();
  }
}

   実行結果
C:\DOCUME~1\MYDOCU~1\MYJAVA~1>javac RunClassWithConstructor.java

C:\DOCUME~1\MYDOCU~1\MYJAVA~1>java RunClassWithConstructor
33.66
139.75
10.0
38.25
137.65
12.0

C:\DOCUME~1\MYDOCU~1\MYJAVA~1>


 「public static void main( String args[] )」で始まるプログラム本体のブロックがずいぶんすっきりしてきました。逆に、メンバ変数の宣言だけだったGeographicInfo3D型の定義がずいぶんにぎやかになってきました。前回までの連載で見てきたように、Javaのクラスは関連するデータをひとまとめに定義するものです。しかしそれだけではありません。クラスには、関連するデータを扱う方法をクラスに所属するメソッドとして定義したり、クラスの初期化をコンストラクタによって半自動化したりするような、さまざまな機能が用意されているのです。

制御文が必要な理由

 メソッドやコンストラクタを使うと、プログラムを効率的に記述できることが分かりました。しかし、メソッドやコンストラクタは処理の記述を簡略化できるだけで、上から順番に処理することは同じです。メソッドを呼び出すとプログラムの処理はメソッドに移りますが、メソッドが終了すればメソッドを呼び出した元の処理に制御が戻ってきます。

 このように単に順番に実行するだけでは、本格的なプログラムを書くのは不可能です。コンピュータに何かを判断させて複雑な処理を実現するには、単に「順番に実行する」だけではなく、「条件に合致したときだけ実行する」とか「繰り返して実行する」という制御が必要になります。そこでJavaには、処理の順番を変えるための「制御文」が用意されています。ほかのプログラム言語を勉強したことがある方なら、「if〜else」や「switch」といった制御文を目にしたことがあるでしょう。

 次回は、こうした制御文と配列型という型を使って、実際に何かの処理を実行するプログラムを作ってみましょう。


Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。