第 2 章 Java 言語の基本的な文法 I 5
3.2 オブジェクトとインスタンス
3.2.5 メソッド
こうした修飾子を「アクセス修飾子(access modifier)」と言いますが、データの入った変数をむやみに外部から覗か れたり、変更されたりすることを防いでいます(アクセス修飾子はこの後でまとめて解説します)。預金口座の金額が 入った変数int myMoneyの値を外部プログラムで勝手に0にされては困りますよね。そんな場合に、変数にprivate 修飾子を付けて、外部から直接アクセスされることを防止しています。
ただ、それでは正規な方法での値の代入などができません。そこで、その値を変更するメソッド (セッター)や、そ の値を取り出すメソッド(ゲッター)を限定公開し、それのみを「利用可」にするわけです(もちろん、そのメソッド内 には正規利用者のアクセスか否かをチェックする機構も付けておかねばなりません)。限定されたメソッドを経由しな いでこのフィールドにアクセスできないようにして、安全を確保する!というわけです。
もう一つ、セッターやゲッターを作る目的が、privateにしたフィールドの名前を自由に変えられる、という利点 です。もしフィールドを外部に自由に使わせるように設定すると、名前の変更はユーザに各自のプログラムの変更を強 制してしまうことになります。代わりに、セッターなどを使えば、(その名前を変更してはなりませんが)フィールド名 の変更や内部構造の変更を外部に影響与えることなく行うことができます。これが次の章で詳しく学ぶ「カプセル化」
です。
一般に、フィールドには全てprivateを付け、ゲッターやセッターはpublic(自由に利用可能)で公開せよ!と言わ れます。
問題 3.2.5. ソースコード3.1のStudent.javaにおける次の変数のスコープ範囲をそれぞれ述べよ。
1. フィールドidNo
2. コンストラクタの引数idNo
3. メソッドprintRecords()の繰り返しカウンタi 4. メソッドprintAverage()の変数sum
この問題より、フィールドの値は、(コンストラクタで初期値を代入したり、メソッドでその値を利用したり)そのク ラス内の全ての要素からアクセスが可能であることがわかります。一方、メソッドの内部で宣言され、そのメソッドの 使用が終わると同時にメモリから消える変数 (iや sum)をローカル変数(local variable) と言いますが、それらはメ ソッドブロックの外部からは利用できませんので、メソッドが違えば同じ変数名を用いることもできるわけです。
public static void main(String[] args) { for(int r=1; r<=10; r++) {
printCircleArea( r );
} }
// 半径 radius の円の面積 area を出力するメソッド static void printCircleArea( int r ) {
double area = r * r * Math.PI;
System.out.printf( "半径=%2d の円の面積は、%8.4f です%n", r, area );
} }
こ の メ ソ ッ ド の 本 来 の 呼 び 出 し 方 は「 ク ラ ス 名 .printCircleArea( r )」で な け れ ば な り ま せ ん 。つ ま り 、 CircleArea2.printCircleArea( r ); が本来の書き方です。しかし、CircleArea2 クラスから見ると、自分の 中にあるメソッドを使うのに毎回自分の名前を付けねばならないというのはちょっと面倒ですよね。そこで、問題(名 前の重複)が起きない限りは、自分のクラス名は省略可能となっているのです。
もちろん、他のクラスから使う場合は、クラス名が必要になります。例えば、Math.cos(double)はMath クラス のクラスメソッドを使っているわけで、Math. は省略できませんでした。しかし、それも面倒な場合がありますよね。
その場合は、import文を用いることになります(import文は後で詳しく)。
クラスメソッドはクラスの持つメソッドなので、インスタンスを作らなくとも利用することができます。一方、イン スタンスメソッドは、インスタンス個々が持つメソッドですから、インスタンスを作って初めて使うことが出来るよ うになります。また、インスタンスごとに処理結果が違ってきます。学生の成績情報プログラムで、Studentクラス のprintRecords()メソッドは、インスタンスメソッドでした(staticが付いていないですね)。同じメソッドでも、
noda.printRecords(); と利用すれば野田さんの情報が出力され、kagura.printRecords();と利用すれば神楽く んの情報が出力されるわけです。
問題 3.2.6. 以下のプログラムはオブジェクト指向とは言い難い(だって、どこにもインスタンスが発生していないか
ら)。そこで、Humanクラスを作り、名前を記憶するString型フィールドnameを与え、コンストラクタに文字列"太 郎"を与えてHumanインスタンス taroを作り、taroのインスタンスメソッドhello に時刻を渡して以下と同じよ うな挨拶の処理を行うように、TaroExample.java を改良(?) しなさい。(ちょっと無理っぽいオブジェクト指向で すが)
ソースコード3.16 TaroExample.java (クラスを作ってみる) package section0302;
public class TaroExample {
public static void main(String[] args) { String name = "太郎";
hello( name, "10:00" );
}
static void hello( String name, String time ) {
System.out.print( name + "さん " + time + " ですね。" );
if( time.compareTo("12:00") <= 0 ) { // 入力時刻が "12:00" 以前なら午前と認識する System.out.println( "おはようございます。" );
} else {
System.out.println( "こんにちは。" );
} } }
問題3.2.7. 以下のmainメソッドが動くように、先に作ったソースコード3.8の平面上の点のクラスPointにインス タンスメソッド public double length()を追加しなさい。なお、平方根はdouble Math.sqrt(double)メソッ ドを使うこと。
ソースコード3.17 (改) PointExample.java package section0302;
public class PointExample {
public static void main(String[] args) {
Point p = new Point(); // デフォルトコンストラクタを用いて点 p を作成 Point q = new Point( 1.5, -0.3 );
p.print(); // p の情報を出力 q.print();
// 原点からの距離を計算して出力する
System.out.println( "原点から p までの距離は、" + p.length() );
} }
問題 3.2.8. 問題3.2.7のPoint クラスに対し次の改良を行い、以下のプログラムが動くようにしなさい。
• クラスメソッド:引数の2点の距離を求めるpublic static double distance( Point, Point )を作る
• インスタンスメソッド:引数の点との距離を求めるpublic double distance( Point ) を作る
• クラス定数:座標(0,0)を与えて点を作り、それをprivate static final Point ORIGINに与える そこで、原点からの距離length()の計算方法をORIGINとの距離を測るものとする
ソースコード3.18 PointExample2.java package section0302;
public class PointExample2 {
public static void main(String[] args) { Point p = new Point( 1.0, 2.0 );
Point q = new Point( 2.0, -1.0 );
// クラスメソッド:引数の2点の距離を返す double dist1 = Point.distance( p, q );
System.out.println( "p と q の距離は(クラスメソッド版):" + dist1 );
// インスタンスメソッド:引数の点との距離を返す double dist2 = p.distance( q );
System.out.println( "p と q の距離は(インスタンスメソッド版):" + dist2 );
} }
問題 3.2.9. 複素数のクラスComplexNumberを完成させ、以下のプログラムが動くようにしなさい。
ソースコード3.19 ComplexNumberExample.java (複素数計算) package section0302;
public class ComplexNumberExample {
public static void main(String[] args) {
ComplexNumber c1 = new ComplexNumber( 1, 2 ); // c1 = 1 + 2i ComplexNumber c2 = new ComplexNumber( 2,-3 ); // c2 = 2 - 3i System.out.println("c1 = " + c1.toString() );
System.out.println("c2 = " + c2.toString() );
// インスタンスメソッドとクラスメソッドで同じ結果を
System.out.println("c1 + c2 = " + c1.add( c2 ).toString() );
System.out.println("c2 + c1 = " + c2.add( c1 ).toString() );
System.out.println("c1 + c2 = " + ComplexNumber.add( c1, c2 ).toString() );
System.out.println("c1 * c2 = " + c1.multiply( c2 ).toString() );
} }
ソースコード3.20 ComplexNumber.java (複素数のクラス) package section0302;
public class ComplexNumber { private double real; // 実部 private double imag; // 虚部
public ComplexNumber( double r, double i ) { this.real = r;
this.imag = i;
}
// 複素数を "( real, imag )" なる形式の文字列で返すメソッド
@Override
public String toString() { // この中身を作る
}
// 自分と c との和の複素数を返すメソッド
public ComplexNumber add( ComplexNumber c ) { // この中身を作る
}
// 引数の2複素数の和を返すクラスメソッド
public static ComplexNumber add( ComplexNumber c1, ComplexNumber c2 ) { // この中身を作る
}
// 自分と c との積の複素数を返すメソッド
public ComplexNumber multiply( ComplexNumber c ) { // この中身を作る
} }
なお、ComplexNumberクラスの10行目の@Overrideは「アノテーション(Annotation:注釈)」と言って(授業で は詳しく触れませんが)、アットマーク @で始まるタグ付けされた付加情報をコンパイラに教える機能です。Java SE 5 で追加されたもので、自作することもできます。@Overrideは、そのメソッドがスーパークラスのメソッドの書き 換えだよ!と言っています。もし書き間違いなどでそのメソッド定義がスーパークラスの定義に反していたときは、エ ラーメッセージが表示されます。他に、@SuppressWarnings("unused")なんてのも、以前出てましたね。
問題 3.2.10. 問題3.2.9の複素数のクラスに次のメソッドを追加しなさい。
• 実部、虚部、それぞれのセッターとゲッターメソッドpublic void setReal(double)など
• 自身の共役複素数を返すメソッドpublic ComplexNumber conjugate()
• 実数で自身を割った複素数を返すメソッドpublic ComplexNumber divide(double)
• 引数の複素数で自身を割った複素数を返すメソッドpublic ComplexNumber divide(ComplexNumber)
• 自身の絶対値を返すメソッドpublic double abs()
• 複素空間での実軸からの角度(偏角)をラジアンで返すメソッドpublic double angle() なお、偏角は、以下で場合分けする:複素数x+iy の偏角θ は、
θ=
arctan
(y x )
(x >0) arctan
(y x )
+π (x <0, y≧0) arctan
(y x
)−π (x <0, y <0)
π/2 (x= 0, y >0)
−π/2 (x= 0, y <0)
=
2 arctan Å√
x2+y2−x y
ã
(y̸= 0),
0 (x >0, y= 0),
π (x <0, y= 0)
で計算され、1つの目の場合わけによる値はMath.atan2(y,x)で得られる。
問題 3.2.11. 先に作った点のクラスPointを利用して、パッケージsection0302内に直線のクラスLineを次のよ うに作成しなさい。
• 直線はax+by+c= 0の標準形で表されるとし、従って、Line クラスは3つ実数フィールドa, b, cを持つ
• 以下の4 種類のコンストラクタを用意する
– 3 つの実数値a, b, cを引数に与えるコンストラクタLine( double a, double b, double c ) – 直線の傾き(実数値)とy軸切片の値(実数値)を引数に与えるコンストラクタLine( double m, double
d )
– その直線が通る適当な 2 つの点 (Point クラスのインスタンス)を引数に与えるコンストラクタLine(
Point p, Point q )。なお、水平線や垂直線も考慮すること
– その直線が通る適当な点 (Point クラスのインスタンス)と直線の傾き(実数値)を引数に与えるコンスト ラクタLine( Point p, double m )。この場合、垂直線は考慮しない
• 引数の点がその直線上にあればtrue を返すメソッドboolean onLine( Point p )
• 引数の点とその直線の距離を返すメソッドdouble distance( Point p )
• その直線が引数の直線と交差するときtrueを返すメソッドboolean isCross( Line other )
• その直線と引数の直線との交点をPoint型インスタンスで返すメソッド Point crossPoint( Line other ) なお、2 直線が重なった場合は交点なしとし、交点がない場合はNULLを返すとする
問題 3.2.12. 問題3.2.11 の各メソッドのクラスメソッド版を以下のように作って、追加しなさい。
• 引数に直線と点を与えて、点が直線上にあればtrueを返すクラスメソッドstatic boolean onLine( Line, Point )
• 引数に直線と点を与えて、それらの距離を返すクラスメソッドstatic double distance( Line, Point )
• 引数に2本の直線を与えて、それらが交差するときtrueを返すクラスメソッドstatic boolean isCross(
Line, Line )
• 引数に2本の直線を与えて、それらの交点を返すクラスメソッドstatic Point crossPoint( Line, Line ) 交点がない場合は、インスタンスメソッドと同様に処理すること
問題 3.2.13. 次のような2次元平面上の円を表わすpublicクラスCircleを作成しなさい。
• privateなフィールドとして、Point型インスタンスcenter(中心点)とdouble型の値r(半径)を持つ
• 引数に中心点と半径を与えて、円のインスタンスを作るコンストラクタpublic Circle( Point, double )
• その円の面積を返すメソッドpublic double area()
• その円の情報を文字列として返すメソッドpublic String toString()。 返す文字列は、”中心点:(1.0,2.0)半径:3.0”のような形式で
• 引数に与えた点と中心点の距離を返すメソッドpublic double distance( Point )。 以下の 2つのメソッドはこのメソッドを使って作る。
• 引数に与えた点がその円の内部(境界も含む)にあるならtrueを返すメソッドpublic boolean includes(
Point )
• 引数に与えた他の円と共通部分を持つ (接する場合も含む) とき true を返すメソッド public boolean isCross( Circle )
問題 3.2.14. 以下のようないろいろな数の加算に対応できる専用クラスMyMath クラスを作り、その中にクラスメ
ソッド plus()を作りなさい。
ソースコード3.21 MyMathExample.java (自作Mathクラス) package section0302;
public class MyMathExample {
public static void main(String[] args) {
System.out.println( MyMath.plus( 10, 20 ) );
System.out.println( MyMath.plus( 10, 20, 30 ) );
System.out.println( MyMath.plus( 10.0, 20.0 ) );
System.out.println( MyMath.plus( new int[]{ 1, 2, 3, 4, 5 } ) );
} }
問題 3.2.15. 以下のプログラムはいずれもエラーが発生します。その理由を述べなさい。
package section0302;
public class Problem0302 { int a;
public static void main(String[] args) { a = 123;
} }
package section0302;
public class Problem0302 { int a;
public static void main(String[] args) { this.a = 123;
} }
では、 mainメソッドの中にどのように書いたら変数aに123 を代入できますか。
逆に、最初のプログラムで mainメソッドの中身a = 123;を変えずにツジツマの合う (?) ようにするには、どうし たら良いでしょう。