Fujitsu The Possibilities are Infinite

 

COBOL技術者のためのJava言語入門
4章:メソッド

[索引]  メソッドの例 |  メソッドの構成 |  データの渡し方 |  mainメソッド |  コマンドライン |  変数のスコープと初期化 |  オーバーロード 

メソッドはCOBOLのPERFORM文(いわゆる外PERFORM文)による呼び出しに似ています。 またCALL文で呼ばれるサブルーチンに近いと言っても良いかもしれません。 Javaのメソッドとこれらとの大きな違いは、メソッドが処理結果の値を1つだけしか持って帰れないことです。 この値のことを戻り値と言います。 Javaではメインプログラムもメソッドの形態をとるので、すべての処理はメソッドとして記述されます。

※ COBOLのPERFORM文では呼び出す側と呼び出される側とで、全データ域を共有しているので入出力パラメーターという概念はありません。 またCALL文ではUSING BY REFERENCE指定で副プログラムとデータの領域を共有することにより自由に値のやりとりができました。 しかし、Javaではプログラム間の結合をなるべく疎にしようとの意図から持ち帰る値は1つとなっています。 入力パラメーター(引き数)にはいくつでも設定できます。

メソッドの例

ここではまず、簡単なメソッドの例を示しましょう。 以下は2つの数を受けとって、その和を返すメソッドです。 先頭のintは戻り値の型です。 このメソッドの結果をint型で返すことを示しています。 次のaddがこのメソッドの名前で、その次の丸括弧 ( ) の中が引き数です。 引き数はデータ型と引き数名とをペアにして書きます。 複数ある時はコンマで区切ります。 次の中括弧 { 以降に処理の中味を記述します。 returnはこのメソッドが返す値を指定するものです。 最後の中括弧 } でメソッドを終わります。

        int add (int n1, int n2) {
          int sum;
          sum = n1 + n2;
          return sum;
        }

メソッドの呼び出し方

Javaには、CALLのように明にメソッドを呼び出す書式はありません。 メソッドの名前を書くだけで、そのメソッドが実行されます。 戻り値を返す時は、以下のようにメソッド自身を変数 c に代入する形で使用されます。 メソッドはこのような形式で使われるので関数と呼ばれることもあります。

        c = add(a, b);

※ メソッドの定義では引き数にデータ型と引き数名とをペアで書きますが、呼び出すときは単に引き数名だけを書きます。 初めのうちは混同しやすいので、よく覚えてください。

メソッドの構成

        修飾子 戻り値の型 メソッド名(引き数のリスト){        
          メソッド本体
        }

メソッドの一般形は上のようになります。 それぞれの項目について、簡単に説明します。 上の書式の1行目書式、すなわち修飾子から丸括弧の終わりまでを、そのメソッドのシグニチャーと呼んでいます。

修飾子

修飾子はメソッドに様々な属性を与えるもので、必要に応じて記述します。 修飾子はクラスにかかわるものが多いので、後のクラスの章で詳しく説明します。

戻り値の型

メソッドが処理結果として持って帰る値(戻り値)の型を記述します。 戻り値が無いときはvoid(ボイド)型としてその旨を表明します。 voidは英語で空(くう)のとか無効のとかいった意味です。 メソッドの本体では、voidの時を除き、ここで記述した型を持つデータをreturn文で返さなければなりません。

※ C言語では、当初は戻り値の型を省略するとint型を示すことになっていました。 しかしそれでは本当に戻り値がない時との区別がつかず、改良してvoid型が作られました。 Javaではこれらの経緯を踏まえて最初からvoid型が規定されています。

メソッド名

メソッド名には自由な名前をつけられます。 但し識別子ですので、使用する文字は1章で述べた命名規則に従う必要があります。

丸括弧 ( )

メソッド名の直後の丸括弧は引き数がなくても、必ず書きます。 それによって変数かメソッドかの区別をしているからです。

引き数のリスト

引き数のリストは、データ型名と引き数名とをペアで記述します。 引き数が複数あるときはこのペアをコンマで区切って書きます。 メソッドを呼び出すときは引き数のデータ型と数、および順序が一致していなければなりません。


【確認問題】 4.1

double型の値 a と b とを受け取り、a と b との積をdouble型で返すメソッド product を作ってください。

データの渡し方 ★

メソッドが呼ばれると引き数の値が(コピーして)メソッドに渡されます。 次のプログラムを見てください。 a と b の値を入れ換えています。

        void swap(int a, int b) {
          int work = a;
          a = b;
          b = work;
        }

しかしこのメソッドを swap(x,y) と呼んでも、x と y の値は入れ代わりません。 メソッドを呼ぶ時には x と y の値をコピーして渡しているだけなので、元の x と y には何の影響も及ぼさないからです。

このように入力パラメーターとして、値そのものを渡すやり方を「値渡し」と言います。 データの渡し方はこの他に、その格納アドレスを渡す「参照渡し」があります(参照とはアドレスのことです)。 Javaのメソッドはすべて「値渡し」になります。

※ COBOLのCALL文ではUSING BY CONTENTを指定すると「値渡し」、USING BY REFERENCEを指定すると「参照渡し」とすることができました。 Javaのメソッドは常に「値渡し」ですが、次に述べるように、事実上「参照渡し」ができるようになっています。

参照型データの受渡し

先に配列のところでも述べたように、基本データ型以外の変数は参照型と言って、値ではなくその値の格納域のアドレスが入っています。 参照型データをメソッドに渡すと、このアドレスが「値渡し」されるので、事実上「参照渡し」になります。

配列を使うと次のように内容を交換するプログラムを記述することができます。

        void swap(int[] a) {    // a[0]とa[1]との値を交換する
          int work = a[0];
          a[0] = a[1];
          a[1] = work;
        }

これを呼ぶプログラムは以下の通りです。 これにより x[0] は 20、x[1] は 10 になります。

        int[] x = {10, 20};
        swap(x);

【確認問題】 4.2

次のメソッドがあります。

        void change(int a, int[] b){
          a = 15;
          b[0] = 4;
        }

このメソッドを呼び出す次のプログラムを実行すると、a と b[0] の値はどうなりますか。

        int a =10;
        int[] b = {1, 2, 3};
        change(a, b);

mainメソッド

独立したJavaのアプリケーションプログラムはmainと言う名前のメソッドから実行を開始します。 mainメソッドのシグニチャは次の通りです。

        public static void main(String[] args)

public

どのプログラムからもアクセスできると言う意味です。 GLOBALの概念に似ています。 詳細はクラスの章で説明します。

static

通常のメソッドは(参照型なので)newしなければメモリ領域に展開されませんが、staticとすると、newしなくても展開されます。 mainメソッドは最初に実行されるので、誰もnewしてくれず、staticにしておく必要があるのです。 詳細はクラスの章で説明します。

void

戻り値が無いことを示します。 mainメソッドでは値を返しても誰も受け取ってくれないので、必ずvoidにします。

main

これはメソッドの名前です。 必ずmainにします。

String[]

OSからJavaアプリケーションを起動するときに指定したパラメーターを、引き数として受け取ります。 その引き数が文字列の配列であることを示しています。

args

引き数の変数名です。 変数名ですから何と書いても良いのですが、このように書くのが慣例です。 argumentsの略です(多分)。 読み方はよくわかりませんが、私(筆者)は「アーギュス」と発音しています。


【確認問題】 4.3

mainメソッドのシグニチャを何も見ずに書いてください。 それぞれの語の意味を言ってください。

コマンドライン

WindowsなどのOSからJavaアプリケーションを起動するときの、一連の命令(コマンド)の書式をコマンドラインと言います。 コマンドラインの例を示します。

        java Sample apple banana        // ☆

上記でjavaはjavaを実行させるコマンド、Sampleがプログラム名、appleとbananaがパラメーターです。 これらを受け取って、プログラムで使うときの変数がargsなのです。 以下にargsの使用例を示します。

        class Sample {
          public static void main(String[] args) {
            System.out.println("最初の引き数は " + args[0] + " です");
            System.out.println("2番目の引き数は " + args[1] + " です");
          }
        }

★ このプログラムは上述のコマンド☆に対し次のように出力します。

        最初の引き数は apple です
        2番目の引き数は banana です

※ C言語では args[0] にはプログラム名が入っていて、args[1] に最初の引き数が入っています。 しかしJavaでは最初の引き数を args[0] で取得しますので、Cプログラマーは注意が必要です。

変数のスコープと初期化

メソッドの中で一時的に使用する変数を宣言することができます。 「データの渡し方」の項のプログラム例で使ったworkなどがその例です。 これらはメソッド内部のみで有効であり、ローカル変数と言います。 ローカル変数は他のメソッドで宣言された同名のローカル変数と衝突しません。

変数のスコープ

ローカル変数という考え方はメソッドに限らず、ブロック(中括弧 { } でくくられた範囲)について有効です。 以前for文のところ(for文内での変数の型宣言)で説明しましたが、for文の有効範囲も中括弧 { } で囲まれているのでブロックです。 このような変数の有効範囲を変数のスコープと言います。

初期化のデフォルト設定 ★

メソッドの外で宣言された基本データ型の変数は、デフォルト値(1章の基本データ型の項参照)で初期化されますが、メソッドの中で宣言された変数は値が入っていないので、初期化せずに使うとコンパイルエラーになります。


【確認問題】 4.4

次のプログラムをコンパイルして実行しようとするとどうなりますか。

        class Sample {
          public static void main(String[] args) {
            int a = 1;
            m1();
          }
          static void m1() {
            System.out.print(a);
          }
        }

オーバーロード

Javaは引き数の数と型が異なれば、同名のメソッドを複数定義することができます。 これをメソッドのオーバーロードと言います。 オーバーロードの機能が無いと処理する引き数の型の違いなどにより、類似した名前のメソッドを作る必要があります。
例えばC言語では、標準ライブラリとして提供されている、絶対値を計算する関数には次の3種があります。

        abs();  int型のデータの絶対値を返す
        fabs(); double型のデータの絶対値を返す
        labs(); long型のデータの絶対値を返す

ところがJavaでは、オーバーロードの機能により、int型、long型、float型、double型のデータの絶対値を計算する4種類のメソッドがすべてabsという1つの名前ですみます。 引き数として与えるデータの型によってコンパイラが選択するからです。

★ オーバーロードをコンパイラが識別するには、引き数の数と型と順序のいずれかが異なっている必要があります。 戻り値の型が異なるだけでは識別できません。 以下の3つのメソッドは同一クラス内では、どの2つをとっても同時には定義できません(コンパイルエラーになります)。

        String method(int x, String str)
        String method(int x, String xyz)
        int method(int x, String str)

【確認問題】 4.5

次のプログラムのうちオーバーロードの関係にあるものを全部挙げてください。

  01:   void meth(int a); int meth();
  02:   int meth(int a); void meth(int b);
  03:   int meth(int x); int meth(int a);
  04:   int meth(int a, String s); int meth(String s, int a);