今まではただひたすらプログラムを続けて記述していましたが、この章では、別のところにあるプログラムを使って、より効率的にプログラムを書けるようになることを目指しましょう。
この章で入力するプログラムとアプリケーションのひな形を作っておきましょう。手順については、これまでの章とまったく同じですので、ここでは手順は示しません。ウィザードの画面のうち、ポイントになる部分だけを示しておきます。クラスの名前を設定する時、パッケージの名前と「public static void main(String[] args)」のチェックを入れておく事を忘れないようにしましょう。
プログラムを単なる1つの流れとして記述するだけでなく、1つのプログラムで同じように記述している部分を1度だけどこかで記述して必要になったときにそれを呼び出すという仕組みがあるのが通常のプログラミングの言語の機能です。ただし、書き方やさまざまな規則は、プログラミング言語ごとによってそこそこ違っていますが、基本的な概念は同じです。ここでは、Javaによるそうした機能を説明しますが、Javaでは「他から利用可能なプログラムの1つの塊」のことを「メソッド」と呼んでいます。他の言語では「関数」と呼ばれる事が多いもので、根本的には同じものです。このメソッドは正しくは次の章で説明するオブジェクト指向の仕組みの1つとして、全体像はそちらで詳細に説明をしますが、この章では、プログラム上で何回でも利用できる1つの塊として説明します。
Javaのメソッドは、1つのクラス、つまり、「class クラス名 { ... }」の { } 内で定義します。この章までは、1つのクラスの中でのプログラミング作業を見てきましたが、ここでも1つのクラスの中での定義方法を説明しましょう。次の章で、一般的な使い方を説明します。メソッドは「どのように定義して作るのか」と、「どのように使えばいいのか」ということの2つの様相があります。
まず、メソッドの基本的な作り方は次の通りです。classによるクラスの中に、次のような形式で、一連のプログラムを作ります。
返り値の型 メソッド名(引数, 引数, ...) {
プログラム;
プログラム;
:
}
一連のプログラムは他のプログラムから呼び出されて、結果を戻します。そのとき、他のプログラムからメソッド内で必要とされる処理を「引数」として渡します。そして、処理した結果は「返り値」として戻されます。返り値は「戻り値」と呼ぶ事もあり、送り仮名の「り」を省略した書き方もされますが、ここでは「返り値」と呼ぶ事にします。1つの見方としては、メソッドに対するインプットが引数、そしてアウトプットが返り値ということになります。
引数は、通常は「変数の型 変数名」という形式で指定をします。引数については1個でも、複数個でも、0個でもかまいません。これに対して返り値は1つだけとなります。メソッドの引数定義で指定した変数は「仮変数」とも言われ、その変数はメソッドの中でのみ利用できることになります。
一方、メソッドを利用する方は、次のように記述します。
メソッド名(引数, 引数, ...)
つまり、単にメソッド名を記述します。そして、上記の「メソッド名(…)」を式の1つの項目として指定でき、実行したときにはその部分が返り値に置き換わると考えてください。返り値はない場合もありますが、その場合は、メソッドは単独で記述されます。引数は定義した側と同じ数で、同じ型で与えるのが原則です。引数がない場合でも()は省略できません。
実際のプログラムでメソッドを定義し、それを使うということを行ってみましょう。作成したクラスCallingについて、次のように必要な部分を書き加えて下さい。プログラムを実行すれば分かりますが、現在の日時が表示されています。日時の扱いについてはいろいろな規則が絡んできますが、ここでは「ともかくこのように書けば現在の日時が文字列で得られる」という方法として、詳細は理解しなくてもかまいません。
package calling;
import java.util.Date; //②
import java.text.DateFormat;
public class Calling {
public static void main(String[] args) {
System.out.println( "現在は" + getCurrentDateTime());
}
private static String getCurrentDateTime() { //①
DateFormat fmtr = DateFormat.getDateTimeInstance();
return fmtr.format(new Date());
}
}
この一連のプログラムでは、2つのメソッドがあります。1つは最初から作られているmain、もう1つは自分で作る事になるgetCurrentDateTimeです。getCurrentDateTimeメソッドは引数はありませんが、そのときも()は省略できません。ただし返り値はString型であることが、メソッド名の定義部分①で分かります。
ここで、private staticというキーワードが返り値の型の前にあります。このうちstaticというのは、ここでは「プログラムに書けば使えるメソッド」と理解しておいてください。通常は次の章に説明するように、メソッド自体使えるようにするためにクラスを実体化しますが、プログラムで書いておく事ですでに使える状態になっているようにするのがstaticというキーワードです。privateあるいはpublicはその定義したものがどの範囲で利用可能かを示します。mainメソッドは、プログラムの最初の実行を行う部分で、システムから呼び出されるのでそこは他のところ(つまりシステム)から利用可能になっている必要があるためpublicと記述します。一方、今作成したgetCurrentDateTimeはこのクラスの中だけで使うのでprivateと記述しておきます。一般には、不必要にpublicにしないというのが原則です。
クラスの定義の前に2行分、importで始まっている箇所②があります。ここはキーワードの最後、DateとDateFormatというのが、Javaの実行システム側で用意されているクラス、言い換えれば「機能」です。これら、日付時刻のデータとそれを文字列として表示する機能を自分が作っているプログラムで利用するために、こうした宣言が必要になります。
getCurrentDateTimeメソッドの2行のプログラムは、ともかくこのように記述すれば、現在の日時が得られると思ってください。そこの部分では、returnというキーワードが最後にあります。これにより、return以降の値を返り値として返します。この場合、「fmtr.format(new Date())」によって現在の日時の文字列が得られるので、それを呼び出し元に戻します。
mainメソッドには1行だけプログラムが存在します。今までのおなじみのコンソールに出力するSystem.out.printlnに続いて出力する文字列が記述されています。ここで、メソッド呼び出しの「getCurrentDateTime()」があり、直下のメソッドを呼び出します。そして、戻り値は「2008/09/28 0:52:09」のような文字列です。この文字列と「現在は」という文字列がつなげられて、コンソールに出力されているのが見えています。
ここでは1度しかgetCurrentDateTimeメソッドは使っていませんが、このような2行程度のプログラムでも、あちこちで使うとなったら都度都度書くのではなくメソッドとしてまとめて、そのメソッドを使えばいいというような状況にしておくことで、プログラムの見通しは良くなります。また、間違える心配も少なくなりますし、変更があればメソッドの1カ所だけを直せばいいということになります。
続いて、引数があるメソッドを定義して使ってみましょう。この章のプロジェクトCallingにあるCallingクラスを、さらに以下のように変更してください。以下、プログラムはすべて記述します。
package calling;
import java.util.Date;
import java.text.DateFormat;
public class Calling {
public static void main(String[] args) {
// TODO 自動生成されたメソッド・スタブ
System.out.println( "現在は" + getCurrentDateTime());
System.out.println( devideRound(5,3,2));
System.out.println( devideRound(8,3,1));
}
private static String getCurrentDateTime() {
DateFormat fmtr = DateFormat.getDateTimeInstance();
return fmtr.format(new Date());
}
private static double devideRound( int a, int b, int c){
double q = (double)a/(double)b;
double factor = Math.pow(10, c);
double r = Math.round( q * factor ) / factor;
return r;
}
}
新たに作ったメソッドdevideRoundは、1番目の引数を2番目の引数で割ったときの商を求めて返り値として返します。このとき、3番目の引数で、少数何桁目で四捨五入するかを決めます。計算自体、少し複雑ですが、まずは、引数と返り値に注目をしてください。引数の定義として「int a, int b, int c」とあるので、これにより、int型の変数a、b、cがメソッドの中で使えると同時に、呼び出し元で指定されたそれぞれの数値が、順番に各変数に代入されます。
つまり、呼び出しもとで「devideRound(5,3,2)」と記載されていますが、これによりもちろんdevideRoundメソッドが呼び出されます。そして呼び出された段階で、変数aは5、変数bは3、変数cは2という値が設定された状態になり、それら呼び出し元で指定された数値がメソッドの中では変数で利用できるということになります。2回目に呼び出される「devideRound(8,3,1)」だと、メソッドの中では変数aは8、変数bは3、変数cは1となるわけです。
devideRoundメソッドについて説明しましょう。最初の変数qでは商を求めていますが、そのままだと整数で計算をするので、double型にキャストしてから計算をしています。次の変数factorは、「10のc乗」を求めています。つまり、c=2ならばfactorの値は100、c=1ならfactorの値は10になります。べき乗を求めるための関数的な機能として「Math.pow」というメソッドがあります。このMath.powはJavaの実行システムに用意されていて、このように記述すると即座に利用できます。
さらに、変数rには、Math.roundによって引数の四捨五入した数値が入力されます。しかしながら、少数部分を四捨五入するだけなので、少数何桁かで四捨五入したいときには、一度factorをかけて四捨五入して、さらにfactorで割ります。たとえば、5÷3=1.666666...です。これに100をかけて166.6666...となります。それにMath.roundで四捨五入した結果は167となります。それをさらに100.0で割る事で最終的に変数rに1.67という数値が求められて代入され、その値が戻される事になります。
同じように引数があるメソッドを定義して使ってみますが、配列が引数です。この章のプロジェクトCallingにあるCallingクラスを、さらに以下のように変更してください。以下、プログラムはすべて記述します。
package calling;
import java.util.Date;
import java.text.DateFormat;
public class Calling {
public static void main(String[] args) {
// TODO 自動生成されたメソッド・スタブ
System.out.println( "現在は" + getCurrentDateTime());
System.out.println( devideRound(5,3,2));
System.out.println( devideRound(8,3,1));
int data[] = { 13,5,2,16,23 };
System.out.println( middle(data) );
}
private static String getCurrentDateTime() {
DateFormat fmtr = DateFormat.getDateTimeInstance();
return fmtr.format(new Date());
}
private static double devideRound( int a, int b, int c){
double q = (double)a/(double)b;
double factor = Math.pow(10, c);
double r = Math.round( q * factor ) / factor;
return r;
}
private static double middle( int array[]) {
int maxValue = array[0];
int minValue = array[0];
for ( int i = 1 ; i < array.length ; i++) {
if (array[i] > maxValue)
maxValue = array[i];
if (array[i] < minValue)
minValue = array[i];
}
return (maxValue+minValue)/2.0;
}
}
メソッドmiddleが新たに付け加わったものです。配列の引数のない書き方が、メソッドの引数のところで見えています。配列名はarrayですが、array[0]とすると最初の要素となります。array[]は何かと言えば、「配列のある場所」という解釈を行います。さらに「int array[]」によって、int型の値が配列として存在する場所ということになります。不特定数の値をメソッドに渡す場合には配列は便利ですが、メソッドmiddleの側では配列dataの値がやってくるというよりも、dataのある場所が変数arrayにセットされて配列として利用できるといったイメージを思い浮かべてください。
従って、ここでは行っていませんが、たとえば、middleメソッド内で、array[0] = 100; といったプログラムがあれば、data[0] の値も書き変わって100になります。このように、呼び出し元のデータを書き換えてしまうようなメソッドも可能です。意図的にこうしたことを行う場合もあれば、それが困る場合もあるわけですが、場合によっては複数の値を戻したいような場合にはこうした手法を使って、返り値だけではなく、呼び出し側の変数に値を残すということもあります。
メソッドmiddleでは、最大値と最小値を求めて、その中間値を求めています。戻り値をdouble型にするために、2で割るのではなく、2.0で割っています。
以下の練習問題は適当にプロジェクトやクラスを作って実行すること。
8-1:文字列型の3つの引数を取ったメソッドを作り、返り値として、各文字列の最初の文字をつなげた3文字を返すメソッドを定義すること。もちろん、そのメソッドをテストするための適当な呼び出しをいくつか作って実行し結果を確かめてみる。
8-2:指定した桁で四捨五入するメソッドroundを作る。引数は2つ指定するが、1つ目はdoubleで四捨五入の対象となる数値を指定する。2つ目はint型で少数以下何桁で四捨五入するかを指定する。同様にテストのために適当な呼び出しを作る事。2つ目の引数が0や負の数でもテストしてみて、どういった結果になるかをチェックすること。
8-3:配列を引数に取り、配列の内容を小さい順から並べた配列を返り値として返すメソッドを作成し、要素数が異なるいくつかの適当な配列をもとに呼び出しを行い、その結果を確認すること。結果を確認しやすくするために、配列の内容を標準出力に書き出すメソッドも作る事。(並べ替えは、プログラムの中でも基礎的ながら重要なテーマであり、本来は講義でやり方を説明してもいいところだが、ググればいろんな言語でいろいろ説明があるので、それを参照しながらやってみること。)
8-4:8-3で作ったメソッドについて、小さな順から並べ替えた配列を返すだけでなく、引数に与えられた配列は逆に大きな順に並べ替えた結果になるようにすること。