プログラム理論と言語 Part2-1-1
1
オブジェクト指向
1.1
効能書
(1)
クラス=「もの」と「もの」に付随した操作系
(2)
クラスのことはクラス内部で処理
ブラックボックス化し、外部仕様を明確化
⇒ 部品としての再利用、安全性
(3)
「もの」と処理の独立性を高め処理も部品化
操作そのものでなく、操作の型情報のみで処理を記述
⇒ コードの抽象化、再利用
⇒ 役割・機能と「もの」を分離し独立性を高める
(4)
複雑さに対処するためにクラス階層として体系化
⇒ 修正・追加・利用(特にライブラリ、ユーティリティ)
処理対象が複雑化すればするほどオブジェクト指向の重要性
は高くなる
チームで大規模システムを構築するときも.
22
JAVA
はオブジェクト指向言語か?
その気になれば、オブジェクト指向でプログラムが書ける
一般に長くなるが、
可読性、安全性、再利用性は高まる
そうでないプログラムも書ける
「体系的でない」ものも多い
簡単な場合はそれでもすむが、複雑になると、
修正・拡張・再利用が困難
プログラムを書くという観点からは、
オブジェクト指向は処理対象と処理を体系化す
るための作法であり、オブジェクト指向言語は
そのための言語的装置
(*)本講義において Javaを題材にする理由: 1. オブジェクト指向の主要な概念は全てJava で説明できること 2. プログラム言語において重要な型概念を十分に講述できること 3. 様々な場所で実際に良く使われている言語であること 4. 実験・演習等をとおして、C言語の理解が進みつつあることを考慮し、Cと構文的に近いオ ブジェクト指向言語であること ポインタについて: Java: 実際は「ポインタのお化け状態」 ポインタを駆使するプログラミングは一般に難しい Java: 比較的に安全にかつ容易に実現 本講義: 上記の事情をある程度説明する3
オブジェクト ⇔ メソッドを持つもの
属性もアクセスメソッドとして関数化して考えるのが標準
配列: これもオブジェクトでデータと関数は一体化
int[] a; a = new int[3];
new int[3]
: 配列領域を割当・生成
a
は領域の先頭アドレスを指すポインタ変数
添え字
i
を与え,
a[i]
のアドレスを計算
32bit
分のデータを取り出す関数
a[i]
: その関数の適用
一般のオブジェクト:
フィールド名を持つ要素からなり
添字の代わりにフィールド名(変数)で要素にアクセス
特定のデータ領域を占有し、
領域全体はオブジェクト参照変数で参照
領域生成は「コンストラクタ」で行う
44
3角形は3つの点からなる: まず「点」を定義
定義は対応するクラスの記述で与えられる
class Point {
//
フィールド変数
double x, y, weight;
//
コンストラクタ(生成方法)
Point(double x, double y,
double w) {
this.x=x; this.y=y;
weight=w;
}
//
インスタンスメソッド
//
各オブジェクトに直接作用
//
オブジェクトと一体化した表記
void show() {
System.out.println(
"x="+x+", y="+y+
" with weight "+weight);
}
}
フィールドアクセス:
this
からは、フィールド名
x
一般に、
a.x
Point a
が参照している
オブジェクトの
x
フィールド
識別子が特定する位置の実体に対し
x
のフィールド値をとりだす
Note: オブジェクトはCの構造体、参照変数は構造体型のポインタ変数に対応する。メソッド(関数) と一体化して使う点がオブジェクト指向を表しているが、上記の図ではその事情を十分説明でき ていない(インスタンスの図)。後で詳しく述べる。5
ポインタ変数と参照変数
オブジェクトを参照する変数(参照変数 と呼ぶ)はCのポインタ変数
と似ているが,
「似て非なるもの」
広義の意味のポインタか? ⇒
Yes
Cと同じポインタか? ⇒
No
アドレス計算を禁止
識別子を用いた 参照機能に限定
変数は,基本型の変数もしくは参照変数
識別子の詳細は
知る必要はない.
知ったとしてもその事実をコードに反映できない
識別子によるアクセスが論理レベルで保障されていれば十分。
以下、実体オブジェクトを特定できる論理的なものとして識別子を考える
66
参照関係と内部状態の変化
厳密表現 :
Point
の参照変数aが参照する(指す)オブジェクト
略した表現:
Point
a (単に、「点」a)
「仮の名前」
: 変数と実体の参照関係は動的に変化。内部状態も!
計算過程: 新規オブジェクトを生成しながら、
(1)
参照関係の変化、
(2)
オブジェクトの内部状態の更新
上記はメソッド実行時
7
メソッド呼出とパラメータ
class Point {
MyVector drawVector(Point ap) {
double vx = ap.x - this.x ; double vy = ap.y - this.y;
return new MyVector(vx, vy); } void nantok(Vector p1) { MyVector mv = p1.drawVector(p2) ... } }
基本データ型の引数: (評価)値を引数パラメータに代入
(
値呼出
)
参照型変数:
仮
=
実 が実行され,仮変数と実変数が同一のオブジェクトを参照
よって,メソッド呼び出しの前後で,内部状態が一般に変更されえる
88
つぎに、3角形を定義する
class Triangle {
//
スタティック変数
static final int nofPoints=3;
//
オブジェクトはフィールドで規定
Point[] points
= new Point[nofPoints];
//
インスタンスメソッド
//
オブジェクトに作用する関数
void show() {
for (short i=0;i<nofPoints;i++)
points[i].show();
}
}//
3角形に対する
show()
を
点に対する
show()
を使って定義
クラス情報
(
型
)
で識別
static final int nofPoints=3;
クラスに固有(インスタンスに依存しない)変数で クラスのスタティックメンバーともいう。 final指定変数は、上書き不可。ただし、下記の注意が必要となる: 1. 基本データ型: 文字通り、「定数」 2. オブジェクト: 参照先オブジェクトは変わらない。しかし、その内部状態は変更される可能 性 「定数」にするにはカプセル化が必要
9
コンストラクタ(オブジェクトの占有領域を作
成)
デフォルト生成:
Triangle t =
new Triangle();
引数なしの特別な手続き
Point[] points
= new Point[nofPoints];
に従い実体を生成し t にセット
この場合、配列領域の確保(割当)
明示的な生成=デフォルト+α
Triangle t =
new Triangle(p0,p1,p2);
右辺実体
(
の
oid)
を
t
にセット
Triangle( //
引数付の手続き
Point p0, Point p1, Point p2) {
points[0]=p0; points[1]=p1;
points[2]=p2;
}
クラスのインスタンス化: コンストラクタを適用し、実体オブジェクトを生成する行為 デフォルトコンストラクタ:引数なしで Triangle() {} クラス定義におけるデフォルトコンスタラクタの扱い 明示的コンストラクタを使用しない⇒ 宣言せずに使える 明示的コンスタラクタを使用 ⇒ デフォルト Triangle()を使うときはそれも明示する 1010
定義・生成・操作
オブジェクトの定義をクラス記述で与え、
コンストラクタでオブジェクトを生成し、
インスタンスメソッドでオブジェクトを操作する
class triangletest {
public static void main(String args[]) {
Triangle t
= new Triangle(
new Point(1,2,3), //
各点実体(の識別子)
new Point(4,5,6),
new Point(7,8,9)
); //
右辺のオブジェクト
(
の識別子
)
を
//
左辺の変数(に代入)に参照させる
t.show(); //
3角形
t
に
show()
を適用
}
//
オブジェクトの操作は手続きで記述
}
//
「手続き的なオブジェクト定義・操作言語」
「論理型のオブジェクト指向言語(OOLP)」、 「関数型のオブジェクト指向言語(Common LISP)」 この他、「オブジェクト指向のデータベース」、 「オブジェクト指向の COBOL!」などもある。 扱うデータの基本単位としてオブジェクトを許すと、こうなる11
様々なメソッドの定義と組合せ:手続きを記述
class Point { // メンバーと呼ばれるフィールドやメソッドを列挙する. // x.length(p) : x が持つメソッド length をパラメータ p で実行させる double x, y, weight; double length(Point p) {// 2点の距離 return Math.sqrt(Math.pow(x-p.x,2)+Math.pow(y-p.y,2)); }// sqrt: 平方根、pow: べき乗。Math クラスが「持つ」スタティックメソッド // Math.pow(...) : Math が持つ pow を実行する ...Point(double x, double y, double weight) {
// クラスをインスタンス化してオブジェクト(構造体)を生成.
// 生成されたオブジェクトは,参照変数で参照し,そのメンバーにアクセスする
this.x=x; this.y=y; this.weight=weight; }
}
class Triangle {
static final int nofPoints = 3; // 大域変数(定数) Point[] points = new Point[nofPoints];
//
double sumOfLength() {// 3つの辺長の総和 double sumOfLength = 0.0;
for (int i=0;i<nofPoints;i++) // i++ は i=i+1 の略記
sumOfLength += points[i].length(points[(i+1)%nofPoints]); return sumOfLength;
} // 整数 n, m に対し、n % m は m を法とした剰余
void show() {....} Triangle(...) {...}
public static void main(String args[]) { Triangle t = new Triangle(
new Point(1,2,3), new Point(4,5,6), new Point(7,8,9) ); if (t.checkTriangle()) System.out.println("辺の長さの総和 = "+ t.sumOfLength()); else { System.out.println("3角形ではありません。要データチェック"); t.show(); }; } } 12
12
用語の整理1: インスタンスとオブジェクト
オブジェクト: 操作の「対象」で、占有領域
(
インスタンス
)
と
それを処理するメソッドを持つ
Cでも構造体のインスタンス化は行う。しかし、そのインスタンスは関数(メソッド)と言語的に一体 化しているわけではない。プログラマが必要に応じて、そのインスタンスへのポインタを関数に 渡して適用・操作するだけの話 一方、Javaでは、最初からインスタンスはメソッドと一体化されて扱われることが大前提であり、そ のことがクラス定義で明示される。それは体系的に操作対象を扱うための流儀であると理解する こと。13
操作により「もの」をクラス分けする
オブジェクトがどのようなメソッドを使用できるかはクラス
定義で決まる。逆に言えば、各オブジェクト毎にメソッ
ドを定義し保持するのは不経済
オブジェクト毎に異なるメソッドが必要な場合:
別クラスの「もの」 ⇒ メソッドがクラスを特徴づける
上図における「人」は識別子と考えてもよい。以後、概念図において「人」記号は、識別子もしくはイ ンスタンス、とする 1414
カプセル化と界面
パッケージとは:クラスの集まり
Java
プログラムは複数のパッケージからなる
カプセル化の話は,パッケージの作り方にも依存する.
ここでは 簡単のため,単一パッケージの場合を説明
private:
クラス内でのみアクセス可 , 無修飾: 他クラスからアクセス利用可
複数パッケージの場合は,
他パッケージからアクセス・利用を許す場合に
public
をつける
class A{private int aint;
//フィールドは原則privateに int aint() {return aint;} //ゲッター A(int x){aint=x;}
//他クラスに公開されたコンストラクタ int subtract(int x){return aint+minus(x);}
//他クラスに公開されたメソッド private int minus(int x){return -x;}
//他クラスからはe.g. (new A()).minus(10)などの
} // 呼び出しは不可
class B {
int h(int x, int y){ A a = new A(x); return a.subtract(y);
}
private B(){} //コンストラクタも隠蔽できる // 他クラスでは Bobject を生成できない public static void main(String args[]){
int a1 = Integer.parseInt(args[0]), a2= Integer.parseInt(args[1]);
System.out.println( new B().h(a1,a2) );
} }
フィールド変数は原則
private
に
(不用意なアクセスによる状態参照・更新を抑制する)
アクセスもメソッドで!
ゲッター
getAttr()
、 +セッター
setAttr(...)
14.1
クラスの外部仕様
外部仕様:クラスとは?
...
○○のメソッドを持つ「もの」
他クラスとの界面を構成する
クラスの外部仕様に関わるメソッドのみ公開し,
外部からは公開メソッドのみで操作
内部的な実装は
private
に隠蔽し独立に管理
ただし, インタフェースの抽象メソッドの実装は
public
public static void main(String[])
クラスは界面を構成するメソッドの実装を与えているとも言える ⇒interfaceへの伏線 オブジェクト指向はさらなる構造化を促す: 独立したクラスの列挙から、構造化されたクラス群 1. クラス階層、インタフェース階層 2. 内部クラス(一般には クラスの nesting) 16
15
整数リスト:再帰的データ構造の例
リスト(要素の並び)x1, x2..., xn 「要素」は「次の要素」へのポインタを持つ リスト: 先頭要素へのポインタ 空リスト: null(要素は指さない) class IntCell {//「要素」の再帰的クラス private int value ;int getValue() {return value;} private IntCell next;
IntCell next() {return next;}
IntCell(int value) {this.value = value;} IntCell(int value, IntCell cell) {
this.value = value; next = cell; }
void showValue() {System.out.print(value+" ");} }
class IntList {
private int size = 0;
private IntCell firstCell = null; void tail() {
firstCell = firstCell.next(); size--;
}
void cons(int x) {
if (size == 0) firstCell = new IntCell(x); else { IntCell nextFirstCell = new IntCell(x,firstCell); firstCell = nextFirstCell; }; size++; } void show() {
IntCell presentCell = firstCell; for (int i=0;i<size;i++) {
presentCell.showValue(); presentCell = presentCell.next(); };
System.out.println("#end_of_list"); }
16
ラッパークラス注意
ラッパークラス: 基本データ型のデータ
をオブジェクト化したもの
オブジェクト: 参照変数で指されるもの
基本データ型のデータ: データそのもの
32 bit
整数なら,
31
ビットの2進表
現と
1
ビット の符号部
右図の場合,
a.intValue()
で,
x
フ
ィールドの値(整数)がとれる.
int b
:
b
は整数型の変数で,
参照変数ではない
言語によっては,ラッパークラスと基本
データ型を区別せずに扱えるものも
ある
class A{public static void main(String[] args) {
System.out.println("Integer.MAX_VALUE = "+Integer.MAX_VALUE); System.out.println("Math.pow(2,31) = "+(int)Math.pow(2,31)); System.out.println("Integer.MAX_VALUE +1 = "+(Integer.MAX_VALUE +1)); } } c:\Users\makoto\home\lecture\pl\resume>java A Integer.MAX_VALUE = 2147483647 Math.pow(2,31) = 2147483647 Integer.MAX_VALUE +1 = -2147483648 18
17
用語の整理2:
obj.m(p1,.., pn)
の読み方
『オブジェクト
obj
に対し、
メソッド
m(p1,.., pn)
を 適用』
『
obj
に対し、
m(p1,.., pn)
を 実行』
『オブジェクト
obj
に対し、
メソッド
m(p1,..,pn)
を 呼び出す』
メソッドはオブジェクトの所有物
本講義では、特に区別しない
この他、オブジェクトをエージェント(処理主体)と考え、
『オブジェクトにメッセージを 送る』
オブジェクトはメッセージに対応するメソッドを持つか
らそれを実行
18
メッセージパッシング
『オブジェクト
obj
(レシーバ)に、
メッセージ
m
をパラメータ
p1,.., pn
で送る』
送られた方は
m
を処理するメソッドを持つ
メッセージ ∼ メソッド
class Teacher {
...
void assignRept(Student st,
Task task) {
Answer ans = st.report(task);
// st
に「
report
せよ」との
//
メッセージを発信し、
//
結果を
ans
で受ける
if (ok(ans)) {...}
else {...};
...
}
class Student {
//
メッセージを受けたときの
//
動作 ∼ メソッドそのもの
Answer report(Task task) {
...
Answer myAnswer
= new Answer(...);
return myAnswer;
//
解答を
myAnswer
で返す
}
}
実際の例題は、FAのシミュレーション(statisfa.java, fa.java)で示す 20メッセージパッシング 例
a君元気かな?
答えが
yes
⇒ 「じゃあ,これやって」
a = new Friend(....);
...
if (a.fine()) a.doTask1(...)
...
Friend
クラスは下記を持つ:
タスク1を実行するメソッド,
自分が
fine
かを判定し答えるメソッド
実行できるタスクが異なる
friend
がいる場合
は,それに応じて 「友人クラス」 は 分化する.
⇒ 後述するクラス階層へ
19
受理機械: メッセージパッシング例
各状態をオブジェクトに. 状態は、受理/非受理の情報を持つ 入力記号列 str を受け取り、次の状態 next に 処理依頼のメッセージを投げ(送り)、next は自分のメソッド process で処理を続ける class State {private static final short nofSymbols = 2; //入力記号は {0,1} private String name;
private boolean accept; // 受理状態情報
private State(String n, boolean f) {name = n; accept = f;} private State[] nextStates = new State[nofSymbols];
void defineNextState(State next0, State next1) { nextStates[0] = next0; nextStates[1] = next1; }
private short head(String str) {return Short.parseShort(str.substring(0,1));} private String tail(String str) {return str.substring(1,str.length());}
private void process(String input) { if (input.length()!=0)
nextStates[head(input)].process(tail(input)); else
if (accept) System.out.println("OK"); else System.out.println("NG"); }
public static void main(String args[]) {
State q0 = new State("q0", false), q1 = new State("q1", false), q2 = new State("q2", true), q3 = new State("q3", false) ; q0.defineNextState(q1, q3); q1.defineNextState(q3, q2); q2.defineNextState(q1, q3); q3.defineNextState(q3, q3); q0.process(args[0]); } } この例題では単一クラスなので,main(String[]) を除き,全てのフィールドとメソッドを privateに しても正しく動作する. 全く同じ理由で,この場合はprivateを付けなくても構わない 22
20
Definition of
A: A
consists of
B
1,
B
2,...
オブジェクト指向: 言語的な定義のスタイルに対応
Aの定義書: 『クラスAのオブジェクトは
クラス(
or
基本型)B
jのフィールドからなり
○○のメソッドを使って操作します』
(構成要素(
part of, has_a
関係)
クラスのメンバー
A
isa
B
, or
A
is defined as a
B
such that ...
A
extends
B
...
「AはBを継承し拡大」
Aに固有なフィールドやメソッドを追加
A
has a role/function of
B
B: インタフェース
A
implements
B
機能を持つ ∼ 役割・機能を実現する手段・メソッドを
具体的に持つ(実装)
⇒ 多態性(ポリモルフィズム)
⇒ 様々な役割や機能を実装クラスで記述
(クラスと機能の分離・独立化を促進)
21
クラス定義のまとめ
class Point {
private double x,y;
Point(double x, double y) { this.x = x; this.y =y; } void show() { System.out.println(" x="+x +", y="+y); } } class Triangle { private static final
int nofPoints=3; private Point[] points
= new Point[nofPoints]; private Triangle(Point p0, Point p1, Point p2) { points[0]=p0; points[1]=p1; points[2]=p2; }
private void show() {
for (short i=0;i<nofPoints;i++) points[i].show(); }
public static
void main(String args[]) { Triangle t = new Triangle( new Point(1,2), new Point(4,5), new Point(7,8) ); t.show(); } //void main(String[]) は } //ここでは Triangle に組み込んだ 実行時: クラスは 生成されたオブジェクトの集まり + 大域変数(static variable)の値 を管理 コンパイル時: オブジェクトの定義書として記述 の集まり。記述は、クラスのメンバー(構成 要素)と呼ばれる変数やメソッドの各々に対 し行われる フィールド変数: オブジェクトの属性 基本データ型もしくはオブジェクト参照変数 フィールド記述がコンスラクタの デフォルト動作を決める インスタンスメソッド: オブジェクトに直接作用する手続き オブジェクト: this で表記(必要ならば) スタティック (static) メンバー スタティック変数:クラスが保持し、 各オブジェクトに依存しない変数 スタティックメソッド: オブジェクトに依存しない手続き
public static void main(String args[]) java クラス名 引数 そのクラスの main が実行される。 クラス毎につけてよい 型は void main(String[])。引数型を別に すると「オーバーロード」 左記では main は Triangle のメンバー 別クラスで main を動作させる場合
Triangle(...) と void show() の private 指定 は外す(メソッドにアクセスできないから)
22
スタティックメンバーについて
オブジェクト指向では、先んずオブジェクトとクラス設計
その次に手続き等の設計
個々のオブジェクトに依存しない定数・変数・メソッドがあ
れば、スタティックメンバーにする
具体にスタティックメンバーが必要となる場面例:
コンストラクタに準じた作用を持つメソッド
クラス固有の定数やオブジェクト(
static final
)
個々の「もの」に依存しないクラス固有の処理手続き
基本データ型に関する型変換や数値関数など
「ラッパークラス」や
Math
で提供される
int Integer.parseInt(String),
double Math.sqrt(double)
23
再帰呼び出しに関する補足
C
同様、
Java
でも再帰は(
static, instance methods
ともに)
使える。関数型でやったようなスタイルでのメソッド定
義もOK。
処理系からみると、再帰と非再帰の区別は全くない!
一般形
f
の定義本体中で別のメソッド
g
を呼び出す
再帰
特に
g = f
なだけで何ら処理は変わらない
ただし、計算量に注意すること
// フィボナッチの数列 f(n)=f(n-1)+f(n-2), f(1)=f(0)=1 class Fibonacci{static int rec_fib(int n) {//この場合、指数爆発 if (n==0 || n==1) return 1;
return rec_fib(n-1)+rec_fib(n-2); }
static int it_fib(int n) {//繰り返し
//定数領域・線形時間 if (n==0 || n==1) return 1;
int nMinus1 = 1, nMinus2 = 1, tmp; for (int i=1;i<n;i++) {
tmp = nMinus1; nMinus1 = nMinus1 + nMinus2; nMinus2 = tmp; };
return nMinus1; }
}
24
スパゲッティプログラム:よくあるパターン
その1
static void main(String args[])
で全てを書こうとし
補足的に
static
メソッドを用いる
⇒ スパゲッティプログラムへの道
処理対象と手続きの両方が構造化されていないため、
⇒ バグとり・修正・再利用・機能の追加が困難、
書き直した方が早い
修正箇所、機能追加、デバッグ箇所を
特定しずらく、ズルズルと全体がついてくる
⇒ スパゲッティ (結局、多くの箇所を「食べないとダメ」、
もしくは、切るのが大変)
25
スパゲッティプログラム:よくあるパターン
その2
Cで書いていたとき、構造体とポインタに苦手意識があり、
その惰性でつい配列だけで書いてしまう。
関連する配列や変数がばらばらで、
関連性を見出すためには「スパゲッティ状態」
コメントをつけて対処する人も多い
⇒ コメントはダラダラとつければ良いというものでない
OOPL
構造体と参照は簡単にかつより安全にカプセル化できる:
⇒ クラスを定義し、メソッドで実体を処理する
2826
スパゲッティプログラム3:例
static int nofItems = 0; // 商品数 static int[] stock = new int[10000],
price = new int[10000]; static String[]
itemName = new String[10000]; ...
static void addItem(int initialStock, int price,
String itemName) { if (nofItems == 10000) {
System.out.println("sorry, full list"); return; }; stock[nofItems] = initialStock; price[nofItems] = price; itemName[nofItem++] = itemName; } まず、重要な定数 10000 のスパゲティ 後で商品コードも必要になった ⇒
追加: static int[] code = int[10000]; 関連するメソッドの引数や本体部の修正 static void addItem(
int initialStock, int price, String itemName, int codeNumber) { ... code[nofItems] = codeNumber; さらに、商品コードで商品リストをソート code の値による並び替えで、同時に、stock, price, itemName 配列も操作 修正箇所が増え、しかもその場所が集中して いるとは限らない 喩え話: 中に色々なものが入っている箱を、他に移動するとき、ある順序で並び替えるときに、箱の中身 をバラバラにして移動、あるいは並べ替えますか? この場合の箱 ∼ オブジェクト 箱の中身 ∼ フィールド クラスによるカプセル化
27
OOPLでは
class Item {
private int stock, price;
Item(int initialStock, int price) { stock=initialStock; this.price=price; }
... その他のメソッド ... }
class ItemList {
private static int MaxNof = 10000; private int nof = 0;
private Item[]
list = new Item[MaxNof]; void add(Item item) {
if (nof != MaxNof) list[nof++] = item; else System.out.println("full list"); } } code や name の追加は対応する フィールドとコンストラク タの修正のみ add(Item) は修正の必要なし 一般に、参照変数で実体を丸ごと 渡すので、関数・メソッドの 引数は少なくなる また、例えば、ソートのときも、 item.code の大小関係で、実 体そのものを並び替えれば それでOK 例えば実体の交換: Item tmp = itemList[i]; itemList[i] = itemList[j]; itemList[j] = tmp; 使い方は、商品名フィールドが追加されたとして、例えば ItemList itemList = new ItemList();
itemList.add(new Item(100,1200,"製図用シャープペン"));
集合やリストは、java.util に色々定義されている。自分で適宜定義し ても良い。インタフェースで話す「キャスト」を使う
class MyList {
private static int MaxNof = 10000; private int nof = 0;
private Object[] list = new Object[MaxNof]; void add(Object obj) {
if (nof != MaxNof) list[nof++] = obj;
else System.out.println("sorry, full list"); }
}
28
オブジェクト指向のストーリー
最初から、必要なオブジェクトとクラスを切り出す作業
⇒ 他のクラスとの切り分け・分節化 ⇒ 部品化
先んず対象のモデリング・設計、
その次にアルゴリズムの設計と解析
その次の次にプログラミング技法
優れたモデル ⇒ 優れたパッケージ(クラスの集合)
⇒ 保守・拡張・利用が楽
29
本日の演習問題1
整数を要素とする線形リスト(要素の並びを表し、参照構造
が一方向のもの)について述べた。一方、CS実験で、
双方向リストをCのポインタで実現する課題を行ったと
思う。課題の手引書と本講義の線形リスト
Java
プログ
ラムを参考にし、実験課題に対する
Java
のクラスを定
めよ
3229.1
演習問題
2-2
下記の2つのメソッドを当該クラスに追加し なさい。ただし、(平面上の)点とベクトルを 区別するために、ベクトルのクラス MyVec-tor を定義し、それを利用した上で下記のメ ソッドを当該クラスに追加すること。 1. Point クラスのメソッド:boolean checkPointOnLine(Point p1, Point p2);
パラメータで与えた2点と自オブジェクトが一直線上にあれば true, otherwise false.
2. Triangle クラスのメソッド: double computeArea(); ベクトルの内積を用いて面積を計算し出力。 演習問題解答: その詳細レベル 仕様をかくつもりで 1. クラス名、クラスの各メンバーの仕様 2. 各メンバーの仕様: (a) 変数であれば、その型(クラスもしくは基本データ型)、 およびその役割・機能の説明をつける (b) メソッドであれば、出力およびパラメータ型を明記した上 で、その動作について正確な日本語で記述すること。た だし、 (1) メソッド中で、(別のオブジェクトに対し)メソッド 呼び出しを行う場合はそのことも明記 (2) スタティックの場合は、static を明示 Java 言語の構文で書けるなら、なお良い