【3/18〜】Amazon、VMwareが語る『クラウドの未来』 スラッシュドット    はてなブックマーク  Yahoo!ブックマークに登録  印刷



第5回 メソッドとコンストラクタはなぜ必要?


平井玄
2003/5/8


今回の内容

プログラムを省略できる「メソッド」
「コンストラクタ」はクラスと同じ名前を持つメソッド
制御文が必要な理由

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

 前回まで、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」といった制御文を目にしたことがあるでしょう。

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






「いまから始めるJava」記事一覧





ホワイトペーパーTechTargetジャパン

Java Solution フォーラム 新着記事

@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

RSSフィード

スキルアップ/キャリアアップ(JOB@IT)

- PR -
- PR -

お勧め求人情報

キャリアアップ 〜JOB@IT
@IT Special -PR-
  TomcatやJBossなどAPサーバ環境に関する
情報を集約! “業務”用APサーバ大百科

New!
  一気に解説! 最新のクラスタストレージ
「RAIDを超えたストレージ基準」……など

New!
  クラウド的ユーザー体験の変化は脅威か?
仮想化技術を使いこなす運用管理術を紹介

New!

  上司や部下、部署内メンバーとの情報共有
を“ガラッ”と変えるコラボツールとは?

New!
  おばかアプリ選手権、第4弾開催中!!
ムダにカッコよくてくだらない作品求ム!

  社内ファイルサーバを“クラウド”に統合
VPN直結「クラウド型ストレージ」を紹介

  Twitterのアカウントはなぜ突破された?
メールによる新手の攻撃手法とその対策

  もう仮想化のお試しフェイズは終わりだ!
Hyper-V 2.0が基幹システムも仮想化

  美人!? まあまあ? 気になる いやし系!!
PV急増で「美人時計」がとった手段とは?

  クライアント企業から求められる人材
⇒IT技術と経営戦略を併せ持つ「戦略家」

  .NET編集長が実践する「技術情報検索術」
サンプル・コードを簡単に探す“技”は?

  業務効率と情報セキュリティ対策を両立!
手間なく確実に機密情報を守る方法とは?

  進化を続ける富士通ストレージETERNUS DX
製品開発者の自信を裏付けるものとは何か

  運用管理の課題を“2つの観点”から分析
ユーザー満足度の高い「仮想環境」とは?

  【CTC事例】約30の基幹システムを統合!
膨大なバッジジョブを制御した方法は?

  仮想化すればコストは削減できるか?
仮想化に必要な「3つの視点」を解説する

  その数、なんと400台以上! グループ内
サーバの「統合管理」によるメリットは?