前回は 「プログラムの中で他のクラスのメソッドを呼び出す」 方法について学習しました。 具体的には、System クラスやMathクラスのメソッドを呼び出す プログラムを書きました。
また、クラスの使い方を調べるために "Java2 Platform, Standard Edition 1.4.0 API仕様" http://nw.tsuda.ac.jp/doc/j2sdk1.4.0/ja/api/index.html に WEB ブラウザでアクセスして、System クラスと Math クラスの 説明を読みました。 クラスに「フィールド」と「メソッド」が含まれることがわかったはずです。
たとえば Math クラスであれば以下の抜粋のように 「フィールド」と「メソッド」に関する説明がありました。
MathクラスのAPI仕様から抜粋フィールドの概要 | |
static double | E 自然対数の底 e にもっとも近いdouble値です |
static double | PI 円周とその直径の比 pi に最も近い double 値です |
メソッドの概要 | |
static double | abs(double a) double値の絶対値を返します。 |
static float | abs(float a) float値の絶対値を返します。 |
static int | abs(int a) int値の絶対値を返します。 |
static long | abs(long a) long値の絶対値を返します。 |
この「フィールド」や「メソッド」とは何なのでしょうか? また、そもそも「クラス」とは一体何なのでしょうか? Javaの本を読むと、「Javaはオブジェクト指向言語である」と書かれてます。 「オブジェクト」とは一体どういうものなのでしょうか?
「一連の動作を行なう部分をひとまとめにして他の場所から 呼び出せるようにしたもの」をJavaでは「メソッド」と呼びます。 他のプログラミング言語では異なる呼び方をして、 たとえばC言語では「関数」と、Pascal言語では「手続き」や「関数」 などと呼びます。
従来のプログラミング言語では、この「メソッド」を主体にして プログラムが作られていました。 まずメソッドがあって、それにデータを与えて計算を進めて行く イメージです。
これではメソッドとデータをバラバラに扱うことなり、たとえば 間違ったデータを与えて間違ったメソッドを呼び出してしまう 「バグ」(= プログラム上の間違い)が 発生しやすくなります。また、将来データの種類が増えたときは 関係するメソッドを全部作り直す必要が発生します。
一方、オブジェクト指向言語では、データとメソッドをまとめた 「オブジェクト」を主体としてプログラムを作って行きます。 データの操作はそのオブジェクトに付属したメソッドを通して行ないます。
このようなオブジェクトを使った方法では
Java言語は「オブジェクト指向言語」です。 次に「オブジェクト」と「クラス」について説明します。
「オブジェクト」は「『データ』と『データの操作方法』をまとめたもの」です。 オブジェクトのことを「インスタンス」とも言います。
「クラス」は「オブジェクトの設計図である」と言うことができます。 すなわち、クラスには
「フィールド」には、「インスタンスフィールド」と「クラスフィールド」が あります。 クラスフィールドはstaticとして宣言されていて、 クラスに共通のデータを保持します。 クラスフィールドにはクラス名から直接アクセスできます (「クラス名 . クラスフィールド」という形式でアクセスできます)。 インスタンスフィールドはstatic宣言がされていないもので、 インスタンス毎に別の値を保持できます。
「メソッド」にも、「インスタンスメソッド」と「クラスメソッド」があります。 クラスメソッドはstaticとして宣言されていて、クラスで共通のメソッドです。 クラスメソッドからは、個々のインスタンスに属するインスタンスフィールド の操作はできません。 クラスメソッドはクラス名から直接アクセスできます (「クラス名 . クラスメソッド(パラメータリスト)」という形式で アクセスできます)。
クラスの概念を図にすると以下のように書けます。
「インスタンスフィールド」、「クラスフィールド」、 「インスタンスメソッド」、「クラスメソッド」の4つを含む クラスの例として 次に Circle クラスの例を示します。
クラスからインスタンスをつくり出す際に、インスタンス フィールドを初期化する必要があります。 そのために『コンストラクタ』と呼ばれるクラス名と同じ メソッドを記述します。コンストラクタは後に続く()の間に 呼び出すときのパラメータを記述します。
Circle.java (Circleクラスの定義例) |
|
RunCircle.java (Circleクラスを使うクラスの定義例) |
|
RunCircle.javaの実行例 |
|
publicと宣言されていますので、クラス外から クラスフィールドの PI にアクセスできます。 アクセスするにはCircle.PI のように、クラス名に . (ドット)をつけて フィールド名を書きます。
publicと宣言されていますので、クラス外からクラスメソッドを 呼び出すことができます。 呼び出すにはCircle.radiansToDegrees(x) のように、 クラス名に . (ドット)をつけてメソッド名を書き、その後に 括弧でくくってパラメータを書きます。
Circle クラスを新しいデータ型として定義したので、 Circle オブジェクトを保持する変数を次のように宣言できます。
Circle p;
Circleオブジェクトを保持する変数を宣言しただけでは、 Circleオブジェクトは生成されません。 オブジェクトを生成するには new 演算子を使います。 new演算子の後にはオブジェクト名と()が続きます。 ()の中にはオブジェクトを生成するためのコンストラクタメソッドに 渡す情報を記述します。 コンストラクタは新しいオブジェクトを生成し、その内部状態を 初期化します。
Circle p = new Circle(5.0); // Circleクラスの新しいインスタンスを生成する
クラスはフィールドとメソッドの集合体を定義したものでした。 クラスとはいわばオブジェクトの設計図と言えます。 クラスのコンストラクタを new 演算子とともに呼び出すことによって、 そのクラスに属するオブジェクトを新しく生成します。 これは設計図に基づいて製品をつくり出すことに相当します。 オブジェクトは、クラスで定義されたインスタンスフィールドの コピーをそれぞれ持っていて、アクセスできます。 また、インスタンスメソッドを呼び出すこともできます。 オブジェクトのフィールドやメソッドにアクセスするには、 変数名の後にドット . を置いてフィールド名やメソッド名を 書きます。 メソッドを呼び出すときにはメソッド名の後に()を書いて、 ()の中にパラメータリストを書きます。
Circle p = new Circle(5.0); // Circleクラスの新しいインスタンスを生成する double radius = p.r; // インスタンスフィールドへのアクセス doubble a = p.area(); // インスタンスメソッドを呼び出す
同じクラスに属するフィールドは直接フィールド名を書くとアクセスできます。 また、同じクラスに属するメソッドは直接メソッド名を書いて指定することが できます。
私達が Sample.java というJava言語のプログラムを書いたとき、 その中で
public class Sample {という記述をしていたのは、実は Sample という 新しいクラスを定義していたのです。 さらに、その中に
public static void main(String args[]) {と記述していたのは、実は Sample クラスに属する main というメソッドを定義していたのでした。
メソッドを定義するには以下のように記述します。
修飾子 型 名前 (パラメータリスト) { メソッド本体 }
return文は、「現在実行しているメソッドを直ちに終了させる」 働きがあります。
メソッドが値を返す場合は、メソッド中に次のような return文を記述します。
return 式;この文を実行したとき、ただちにこのメソッドは実行を終了し、 メソッドの返り値として「式」を計算した結果が返されます。
メソッドが値を返さない場合は、メソッド中に次のような return文を記述します。
return;この文を実行したとき、ただちにそのメソッドは実行を終了しますが、 何も値は返されません。 メソッド本体の最後の文を実行し終ったときは、 メソッドの実行を終了し、メソッドを呼び出した式に戻ります。 これはすなわち、メソッド本体の最後に値を返さない
return;というreturn文が暗黙的におかれていることになります。
メソッドを呼び出すときは、メソッド名の後に()をつけ、 その括弧の中にパラメータリストを書きます。 パラメータとしては「式」を指定できます。 メソッドに渡される情報は、式の計算結果の値が コピーされたものです。
すなわち、メソッドを呼び出すときに、実引数として (int, double, booleanなどの)基本データ型のデータを渡すと、 それはコピーして渡されますので、メソッドの中で変更しても 元のデータは変更されません。
もし int add1(int) というメソッドがあったとしてその定義が 次のようであるとします。
int add1(int x) { x = x + 1; return x; }このときに、もしメソッドを呼び出す側で以下のように書くと
int a, b; a = 2; b = add(a); System.out.println("a="+a+",b="+b);画面には a=2, b=3と表示されます。変数aの値は 変更されないのです。
Java言語の文法では「メソッドの呼び出し」自体も「式」となります。 「メソッドの呼び出し」という「式」の値は、メソッドが返す「返り値」です。
上では、単に「パラメータ」(または引数)と呼んでいましたが、 特にメソッドの定義のときに書くパラメータ(引数)を 「仮引数」(かりひきすう)といいます。 またメソッドを呼び出すときに実際に渡すデータを 「実引数」(じつひきすう)と呼びます。
開き中括弧 { と 閉じ中括弧 } で囲まれた部分をブロックと呼びます。 『メソッドにおける本体の定義』や、『複文』は { と }を使って、 すなわち、ブロックで表されています。
「変数」はブロックの中で宣言することができます。 Javaは変数の宣言に関して比較的柔軟でブロックのどこにでも 書くことができますが、変数の有効範囲はそのブロックの中だけです。 変数が有効である範囲のことを「通用範囲」(または、scope、スコープ) といいます。
メソッド定義のときに、パラメータ(仮引数)として宣言された変数や、 メソッドの本体の中で定義された変数は、そのメソッドの中だけで有効です。 他のメソッドの中に同じ名前の変数があっても全く関係がない (=全く別の変数として取り扱われる)ことに注意が必要です。
「体重と身長から BMI を計算する」というプログラムを以前作成 しました。 これをクラスを使って書き直してみます。
Profile1.java |
|
RunProfile1.java (Profile1クラスを使うクラスの定義例) |
|
RunProfile1.javaの実行例 |
|
まず MyGraphics.java をダウンロードして下さい(マウスの右ボタンを使って「対象を ファイルに保存」を選ぶこと)。 javaのプログラムを作るフォルダに保存して下さい。
GSample07.java が正しく動作するように、MyGraphics.java に public void drawRectangle(int sx,int sy,int w,int h,int r,int g,int b) というインスタンスメソッドの定義を追加して下さい。 ただし、これは座標(sx,sy)の点を左上の点とし、幅 w、高さ h の 四角形を描くメソッドです。
正しく動作することを確認したら、変更後の MyGraphics.java を 提出して下さい。
MyGraphics.java (一部省略) |
|
GSample07.java |
|
GSample07.javaの実行例 |
|
MyGrahpics.java にいろいろなインスタンスメソッド (たとえば3角形などを描くメソッドなど)を追加して下さい。 MyGraphics.java に追加したいろいろなメソッドを呼び出して美しい 絵を描く GSample08.java を作って下さい。
「1年ゼミ提出課題3」に MyGraphics.java を、 「1年ゼミ提出課題4」に GSample08.java を提出して下さい。