[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

情報処理IIIA(Javaプログラミング入門)第8回

目次 索引
8.1 手続きとしてのメソッド
8.1.1 メソッドとは
8.1.2 メソッドの引数
8.2 関数としてのメソッド
8.2.1 メソッドの返り値
8.2.2 メソッドの使用例
8.3 演習8
8.4 レポート課題
値呼出し  返り値  仮引数  実引数  引数  メソッド  メソッド呼出し 

8.1 手続きとしてのメソッド

8.1.1 メソッドとは

今、次のようなプログラムを考えます。

/* 1*/ class NameTag1 {
/* 2*/     public static void main (String[] args) {
/* 3*/         System.out.println("-------------------------");
/* 4*/         System.out.println("-------------------------");
/* 5*/         System.out.println("No.");
/* 6*/         System.out.println("-------------------------");
/* 7*/         System.out.println("Name");
/* 8*/         System.out.println();
/* 9*/         System.out.println("-------------------------");
/*10*/         System.out.println("-------------------------");
/*11*/     }
/*12*/ }
b00a001@Ampere:~/java% java NameTag1
-------------------------
-------------------------
No.
-------------------------
Name

-------------------------
-------------------------
b00a001@Ampere:~/java%

3行目、4行目、6行目などに、罫線を出力するという、まったく同じ処理が繰り返されています。 これらの処理に名前をつけ、その名前で処理が表せますと、プログラムが読みやすく、かつ書きやすくなります。 メソッドというものを用いますと、そのようなことができます。 なお、メソッドには「インスタンスメソッド」というものと「クラスメソッド」というものがあります。 今日はクラスメソッドのみ扱います。

メソッドmethod ) とは、処理のまとまりに名前をつけたものです。 メソッドを使うためには、プログラムの中でメソッドを定義しなくてはいけません。 メソッドの定義は次のような形をとります。

static void methodname () {
    statement; ...
}

これによって、処理のまとまり statement ; ...methodname という名前がつきます。

定義したメソッドを使うには、プログラムに次のような文を書きます。 これを メソッド呼出しmethod call ) とよびます。

methodname();

プログラムの中にメソッド呼出しがありますと、実行の流れは次の図のようになります。 メソッド呼出し f(); を実行するとき、メソッド f の定義の内容を実行し、それが終わったら、メソッド呼出しの次から実行します。

Execution flow on a method call
図 8.1  メソッド呼出しにおける実行の流れ

上記のプログラムの場合、次のようになります。 罫線を出力するという処理に、 rule という名前をつけています。 メソッド呼出し rule(); を実行しますと、罫線が出力されます。

/* 1*/ class NameTag2 {
/* 2*/     public static void main (String[] args) {
/* 3*/         rule();
/* 4*/         rule();
/* 5*/         System.out.println("No.");
/* 6*/         rule();
/* 7*/         System.out.println("Name");
/* 8*/         System.out.println();
/* 9*/         rule();
/*10*/         rule();
/*11*/     }
/*12*/     static void rule () {
/*13*/         System.out.println("-------------------------");
/*14*/     }
/*15*/ }

メソッド定義の中では、他のメソッドを使うこともできます。 次の例では、罫線を出力するメソッド rule を使って、二重の罫線を出力するメソッド doubleRule を定義しています。

/* 1*/ class NameTag3 {
/* 2*/     public static void main (String[] args) {
/* 3*/         doubleRule();
/* 4*/         System.out.println("No.");
/* 5*/         rule();
/* 6*/         System.out.println("Name");
/* 7*/         System.out.println();
/* 8*/         doubleRule();
/* 9*/     }
/*10*/     static void rule () {
/*11*/         System.out.println("-------------------------");
/*12*/     }
/*13*/     static void doubleRule () {
/*14*/         rule();
/*15*/         rule();
/*16*/     }
/*17*/ }

8.1.2 メソッドの引数

メソッドには引数を与えることができます。 引数argument ) とは、メソッドに対する入力データだと思ってください。 なお、コマンドライン引数はJavaアプリケーションの引数で、アプレット・パラメタはJavaアプレットの引数です。

引数を持つメソッドは次のように定義されます。 argument の部分に、引数を表す変数を書きます。

static void methodname (int argument, ...) {
    statement; ...
}

このようなメソッドを呼び出すには、次のように書きます。 argument の部分に、引数となる式を書きます。

methodname(argument, ...);

メソッド定義の引数を 仮引数formal argument ) とよび、メソッド呼出しの引数を 実引数actual argument ) とよびます。 メソッドは呼び出されますと、仮引数(変数)に実引数(式)の値が代入され、メソッド定義の内容が実行されます。

次の例では、 n 重の罫線を出力するメソッド multiRule を定義しています。 メソッド呼出し multiRule(2); が実行されますと、メソッド定義の仮引数 n に実引数の値2が代入され、罫線の出力を2回繰り返すようにメソッドが実行されます。 なお、メソッド定義の中でも変数が宣言できることに注意してください。 (11行目)

/* 1*/ class NameTag4 {
/* 2*/     public static void main (String[] args) {
/* 3*/         multiRule(2);
/* 4*/         System.out.println("No.");
/* 5*/         multiRule(1);
/* 6*/         System.out.println("Name");
/* 7*/         System.out.println();
/* 8*/         multiRule(2);
/* 9*/     }
/*10*/     static void multiRule (int n) {
/*11*/         int i;
/*12*/         for (i = 0; i < n; i++) {
/*13*/             System.out.println("-------------------------");
/*14*/         }
/*15*/     }
/*16*/ }

メソッドを呼び出すとき、仮引数には実引数の値が代入されます。 詳しく言いますと、呼出しの際、仮引数の変数が用意され、そこに実引数の値のコピーが格納されます。 このような呼び出し方を、 値呼出しcall by value ) とよびます。

次のプログラムは、引数を0にしようとしてもうまくいかない例です。

/* 1*/ class BindingTest {
/* 2*/     public static void main (String[] args) {
/* 3*/         int x = 100;
/* 4*/         setZero(x);
/* 5*/         System.out.println(x);
/* 6*/     }
/* 7*/     static void setZero (int n) {
/* 8*/         n = 0;
/* 9*/     }
/*10*/ }
b00a001@Ampere:~/java% java BindingTest
100
b00a001@Ampere:~/java%

4行目のメソッド呼出し setZero(x); を実行しますと、メソッド定義の7行目の変数 n にデータ100のコピーが格納されます。 コピーを0に置き換えてメソッドを終了しますので、5行目の変数 x の値は100のままなのです。


8.2 関数としてのメソッド

8.2.1 メソッドの返り値

メソッドは、まとまった処理をするだけでなく、何らかの計算をして、その値を呼び出し側に返すこともできます。 そのような値を、メソッドの 返り値return value ) とよびます。 返り値は、メソッドの出力データと考えてください。

整数( int 型)を返り値に持つメソッドは、次のように定義されます。

static int methodname (int argument, ...) {
    statement; ...
}

これまでの、返り値を持たないメソッド定義での void の代わりに、 int と書きます。 この定義の中に、少なくとも一つ return 文を書きます。 return 文は次のような形をとり、式 expression の値がこのメソッドの返り値になります。

return expression;

返り値を持つメソッドは、次のような式で呼び出します。

methodname(argument, ...)

式の中にメソッド呼出しがありますと、メソッド定義の内容が実行されます。 そして、その中の return 文が実行されますと、メソッドの実行は終了され、 return 文の式の値が呼び出し側に返されます。

次の例では、二つの数のうち大きい方を返すメソッド max を定義しています。 返り値を持つメソッドは、式として呼び出されることに注意してください。

/* 1*/ class MaxTest {
/* 2*/     public static void main (String[] args) {
/* 3*/         System.out.println(max(100, 200));
/* 4*/         System.out.println(100 + max(20 + 30, 30));
/* 5*/     }
/* 6*/     static int max (int m, int n) {
/* 7*/         if (m > n) {
/* 8*/             return m;
/* 9*/         } else {
/*10*/             return n;
/*11*/         }
/*12*/     }
/*13*/ }
b00a001@Ampere:~/java% java MaxTest
200
150
b00a001@Ampere:~/java%

注意: 返り値を持たないメソッドでも return 文が使えます。 この場合、式を抜いた return; という形になります。 メソッドの実行中に return 文を実行しますと、そこでメソッドの実行を終了します。

8.2.2 メソッドの使用例

メソッドの例として、はじめに mn 乗を計算するメソッド power を定義します。 ここで、 n ≧0 と仮定します。

/* 1*/ class PowerTest {
/* 2*/     public static void main (String[] args) {
/* 3*/         System.out.println("power(2, 5) is " + power(2, 5));
/* 4*/         System.out.println("power(5, 2) is " + power(5, 2));
/* 5*/         System.out.println("power(3, 3) is " + power(3, 3));
/* 6*/     }
/* 7*/     static int power (int m, int n) {
/* 8*/         int i, result = 1;
/* 9*/         for (i = 0; i < n; i++) {
/*10*/             result = result * m;
/*11*/         }
/*12*/         return result;
/*13*/     }
/*14*/ }
b00a001@Ampere:~/java% java PowerTest
power(2, 5) is 32
power(5, 2) is 25
power(3, 3) is 27
b00a001@Ampere:~/java%

次の例は、階乗を計算するメソッド fact です。 ここで n の階乗とは、 n ! と表され、

n ! = 1×2×…×( n - 1)× n

と定義されるものです。 ( n ≧0 と仮定しています。)

/* 1*/ class FactTest {
/* 2*/     public static void main (String[] args) {
/* 3*/         System.out.println("fact(4) is " + fact(4));
/* 4*/         System.out.println("fact(5) is " + fact(5));
/* 5*/         System.out.println("fact(6) is " + fact(6));
/* 6*/     }
/* 7*/     static int fact (int n) {
/* 8*/         int i, result = 1;
/* 9*/         for (i = 1; i <= n; i++) {
/*10*/             result = result * i;
/*11*/         }
/*12*/         return result;
/*13*/     }
/*14*/ }
b00a001@Ampere:~/java% java FactTest
fact(4) is 24
fact(5) is 120
fact(6) is 720
b00a001@Ampere:~/java%

最後の例は、塗り潰した長方形を2つ描くアプレットです。 塗り潰した長方形を描くには、 g.fillRect( ... ); を使うことはすでに説明しました。 ここではこれを用いず、自作のメソッドで長方形を塗り潰すことにします。

アプレットでメソッドを定義する場合、そのメソッドが直線を描いたりするときは、仮引数に Graphics g を追加する必要があります。 そして、メソッド呼出しでも、実引数に g を追加します。

自作するメソッドの名前は myFillRect とします。 引数は、まず Graphicsg , そして長方形の左上の座標 x , y , 幅 width および高さ height とします。 このメソッドは値を返しませんので、 static void と指定します。

長方形は、上から順にすきまなく水平線を引いていけば、塗り潰すことができます。

メソッド定義のアルゴリズムは次のようになります。

  1. ループ制御変数 i を宣言する。
  2. i の値を 0 から height 以下の間増加させながら以下を繰り返す。{
  3.   座標 ( x , yi ) から右に長さ width の水平線を引く。

メソッド呼出しのアルゴリズムは次のようになります。

  1. 引数を g ,(左上の座標)50, 50,(幅)150,(高さ)50 としてメソッド myFillRect を呼び出す。
  2. 引数を g ,(左上の座標)100, 100,(幅)50,(高さ)50 としてメソッド myFillRect を呼び出す。

プログラムは以下のようになります。

/* 1*/ import java.applet.*;
/* 2*/ import java.awt.*;
/* 3*/
/* 4*/ public class FillRectangle extends Applet {
/* 5*/     public void paint (Graphics g) {
/* 6*/         myFillRect(g, 50, 50, 150, 50);
/* 7*/         myFillRect(g, 100, 100, 50, 50);
/* 8*/     }
/* 9*/     static void myFillRect (Graphics g, int x, int y,
/*10*/                             int width, int height) {
/*11*/         int i;
/*12*/         for (i = 0; i <= height; i++) {
/*13*/             g.drawLine(x, y + i, x + width, y + i);
/*14*/         }
/*15*/     }
/*16*/ }
Rectangles by a hand-made method
図 8.2  自作メソッドによる長方形

8.3 演習8

塗り潰した台形を組み合わせることにより、塗り潰した五角形(Pentagon)を描くアプレット(縦横200ピクセル)を作成してください。 組み合わせ方は下図の通りとします。 上の部分は三角形ですが、これは上底の長さが0の台形と考えることにします。

Partition of a pentagon
図 8.3  五角形の分割
表 8.1  五角形の座標
A B C D E
x座標 100 24 53 147 176
y座標 20 75 165 165 75

プログラムは、塗り潰した台形を描くメソッドの定義と、そのメソッド呼出しから構成されます。 メソッドの名前は fillTrap とし、引数は Graphicsg , 台形の左上の座標 x 0 , y 0 , 上底の長さ w 0 , 左下の座標 x 1 , y 1 , および下底の長さ w 1 とします。 このメソッドは値を返しません。

Coordinates of a trapezoid
図 8.4  台形の座標

台形は、すきまなく水平線を引くことによって塗り潰します。 水平線の y 座標が与えられたとき、左端の x 座標とその長さ w は、

xx 0 +( x 1x 0 )×( yy 0 )/( y 1y 0
ww 0 +( w 1w 0 )×( yy 0 )/( y 1y 0

となることが数学的に導かれますので、この公式を利用します。

メソッド定義のアルゴリズムは次のようにします。

  1. 座標を表す変数 x , y , および水平線の長さを表す変数 w を宣言する。
  2. y の初期値を y 0 とする。
  3. yy 1 以下の間、以下を繰り返す。{
  4.   公式にしたがって計算し、 x の値をその計算結果とする。
  5.   公式にしたがって計算し、 w の値をその計算結果とする。
  6.   座標 ( x , y ) から右に長さ w の水平線を引く。
  7.    y の値を1増やす。

メソッド呼出しのアルゴリズムは次のようにします。

  1. 引数を g ,(左上の座標)100, 20,(上底の長さ)0,(左下の座標)24, 75,(下底の長さ)176−24 としてメソッド fillTrap を呼び出す。
  2. 引数を g ,(左上の座標)24, 75,(上底の長さ)176−24,(左下の座標)53, 165,(下底の長さ)147−53 としてメソッド fillTrap を呼び出す。

余力のある人は、以下の図表を参考にして、塗り潰した星形を描いてください。 メソッド定義は、五角形のときと同じものを使います。

Partition of a star shape
図 8.5  星形の分割
表 8.2  星形の座標
A B C D E F G H I J K L
x座標 100 82 24 71 53 100 147 129 176 118 64 136
y座標 20 75 75 109 165 131 165 109 75 75 131 131

8.4 レポート課題

今日の演習8に従ってJavaプログラムを作成し、そのプログラムをkonishi@twcu.ac.jpあてにメールで提出してください。 メールには、学生番号、氏名、科目名、授業日(11/14)を明記してください。


[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

2002年11月14日更新
konishi@twcu.ac.jp
Copyright (C) 2002 Zenjiro Konishi. All rights reserved.