TSSI 基盤技術研修コース— プログラミング言語 — ＃ 2
久野靖∗

2004.4.23

1

はじめに

1.1

前回の皆様のアンケートを拝見して…

• モジュール• 抽象データ型• オブジェクト

□ 興味深かった点は人によってまちまちでしたが、プログ• Java における保護 (クラス単位、パッケージ単位)

ラミング言語やそのさまざまな機能について改めて考える機会になった、という意見が多かったです。

2.1□ 実習しながら聞きたいというご意見も頂きましたがそれモジュール□ モジュール以前の言語→手続きが単位

は時間がとてもかかるので…

• 手続きが単位だと「カタマリ」が小さすぎる• 講義という TSSI の形態からして、皆様のご希望にそえない部分もあるのはしかたないと考えます。部分• 複数の手続きが 1 つのデータ構造を共有する→よく的にでも有益な内容があればそこを活用して頂くとあるパターン

いうことで。

• もともと Java 言語を詳細にやるという趣旨ではないのでそこもご了承を…

関数

関数

1.2

第 2 回の構成

データ関数□ 「モジュールと保護」の話からはじめて改めてオブジェ
クト指向を導入□ そのデータ構造は「グローバル変数」じゃいけないの?

□ オブジェクト指向言語の進化と諸概念を並行して見て行□ グローバルだと「よそからいじられる」可能性

く (歴史的な順序におおむね従った方がわかりやすい)□ オブジェクト指向言語の実装技術→必要な箇所にそのつ• データ構造→通常、「こういう性質を維持」という条

ど挿入 (諸概念が出て来たところで説明した方が納得し件がある (整合性条件) →これが壊れるとそれを前提やすい)としているコードも破綻□ オブジェクト指向言語の利用技術→最後にまとめて解説• グローバルだとそれを分からずにいじられてしまう□ Java による例題/演習→それに適した話題のところに適□ モジュール機構があれば、データ構造は特定のコードか

宜挿入

らだけしかいじれなくできる□ Java 以外の言語の例も一部出て来る→言語の対比の必• 整合性条件の維持→そのデータをいじるコードが限定されていると、そこだけきちんとすれば確実に大

要上

丈夫

2

モジュールと保護• さらに、外から直接呼ばれたくない関数も同様にして限定できるとよい。

□ 前回スキップした内容を含め、オブジェクト指向の導入□ どうやって実装するか?

になる概念を見て行く

言語にモジュールがあれば簡単だが、C 言語でも「ファイル内の static」を使ってある程度できる∗ 筑波大学大学院経営システム科学専攻

1

□ 例:

□ なぜこれが「抽象データ型」かというと…GraphWin 型「図形描画できるウィンドウ」というデータ型を新しく作っていると考えられる。たと---GraphWin.c--#include <なんとか.h>static Window *win;
static struct { int x1,y1,x2,y2 } line[1000];
static int linecount;static int initialized = 0;
static int init() {win = ...
linecount = 0;}
int addline(int x1, int y1, int x2, int y2) {if(!initialized) init();line[lincount].x1 = x1; line[lincount].y1 = y1;line[lincount].x2 = x2; line[lincount].y2 = y2;++linecount;}
int update() {int i;
if(!initialized) init();for(i = 0; i < linecount; ++i) 線 i を描く;}...

えば利用側は:---GraphWin.h--typedef void *GraphWin;
GraphWin create_gw();
int gw_addline(GraphWin gw, int x1, int y1, int x2, int y2)
int gw_update(GraphpWin gw);...
---アプリケーション.c--include <GraphWin.h>
int main() {GraphWin gw = create_gw();...
gw_addline(gw, ...);...
gw_update(gw);...
}□ この「2 回ずつ言う」あたりを何とかしたのがオブジェ
クト指向の「メッセージ送信記法」と考えてもらえれば□ このコードでは「どういう整合条件」があると思うか?

よい。

□ こうしておくと他にどういういいことがあるか?
□ 結局、モジュールの機能は「何もしなければアクセスで

2.3

きるものを隠す」こと→カプセル化 (encapsulation)

オブジェクト指向□ いちいち上のようにヘッダファイルを使い分けたり、第• 「わざわざ不自由にすること」が実は重要

1 引数でデータ構造のポインタを渡したりすると煩雑→
間違い→破綻。

2.2

抽象データ型□ これを言語の方で自動的にやってくれると嬉しい□ モジュールでは隠す対象は「データ一式」□ では窓を沢山作りたければどうする?

class GraphWin {Window win;Line[] line = new Line[100];
int linecount;public GraphWin {win = ...;
linecount = 0;}
public addLine(int x1, int y1, int x2, int y2) {line[linecount++] = new Line(x1,y1,x2,y2);}public upate() {for(int i = 0; i < linecount; ++i) 線を描く…
}...
class Line {int x1, y1, x2, y2;public Line(int px1, py1, px2, py2) {x1 = px1; y1 = py1; x2 = px2; y2 = py2;}
public int getX1() { return x1; }public int getY1() { return y1; }public int getX2() { return x2; }public int getY2() { return y2; }}}→抽象データ型---GraphWin.c--#include <なんとか.h>typedef struct {Window *win;
struct { int x1,y1,x2,y2 } line[1000];
int linecount; } GraphWin;
GrappWin* create_gw() {GraphWin *gw = (GraphWin*)malloc(siezof(GraphWin));gw->win = ...
gw->linecount = 0;
return gw;}
int gw_addline(GraphWin *gw,
int x1, int y1, int x2, int y2) {gw->line[lincount].x1 = x1;
gw->line[lincount].y1 = y1;
gw->line[lincount].x2 = x2;
gw->line[lincount].y2 = y2;++gw->linecount;}
int gw_update(GraphWin *gw) {int i;
for(i = 0; i < gw->linecount; ++i) 線 i を描く;}...

□ 使う側では次のようになる2□ さらに!

import ...
public class Test {public static void main(String[] args) {GraphWin gw = new GraphWin();...
gw.addLine(...);...
gw.update();}}ソッド) についても次の 4 つの「保護レベル」がある (4つの p)• public → パッケージ外からでも参照できる• 無指定 → パッケージ内からだけ参照できる• private → そのクラス内からだけ参照できる• protected → 無指定プラス、そのクラスのサブク
ラスから参照できる (これについては少し後で)• 初期設定とかポインタとかに煩わされなくなった• メッセージ送信記法→「馬から落馬」がなくなった

2.4

あるクラスの中に含まれているもの (変数、メ□ おすすめの保護設定は…

• 変数は原則として private にするのがよい (カプセJava の保護機構

ル化)。final 指定 (変更不可) なら public でもよい

• メソッドは必要に応じて private/無指定/public のどれかに□ このように、「個別のデータを」「クラス内外で保護する」
まではできた□ しかし今度はオブジェクト指向言語になると「クラスが □ 実際は状況に応じてさらに考える…
山のようにできる」状態に□ このため、「このクラスはどこからでも使う」
「このクラ

2.5

スはここだけの作業用」という区別をしたくなった

本節のまとめ• 入れ子クラス (クラスの中にクラスを入れられる) で □ 「本来見えるものを隠すこと」はプログラム言語では重要
十分?
□ 入れ子クラスは「ある特定のクラス内だけで使う下請け」□ 歴史:モジュール (1 個) →抽象データ型 (複数) →オ

ブジェクト指向 (多数) →クラス群ないしパッケージにはベスト□ 多数のクラスの構造化についてはまだ先があるかも…

• 「複数のクラスで共通に使う下請け」にはちょっと使いづらい• 「ブロック構造」と「モジュール」の違いのようなオブジェクト指向言語とその諸概念

3もの□ オブジェクト指向の基本的なアイデアは簡単□ このため「パッケージ」という単位をさらに設けた□ 実際に言語として設計すると多くの考慮点• Java ではすべてのクラスは「パッケージ内のみで参 □ 歴史的な推移に従って見ていくと分かりやすい
照可能」か「public(パッケージ外から参照可能)」
かのどちらかになる

3.1• ついでに…Java では「public 指定のクラスはそのクラス名と同じ名前のファイルに入れないとコンパ□ オブジェクト指向とは、プログラムが扱う対象のそれぞ

イルできない」という規則。

れを自立した「もの」として扱う「考え方」• 逆に言えば、public でないクラスなら一緒に入れて□ オブジェクト指向が取り入れられている分野はいろいろも構わない (class ファイルはどのみちクラスの数あるだけ生成される)。
パッケージ

パッケージ

publicclass
Apublic
class
X

class
B

public
class
Y

class
C

class
Z

オブジェクト指向の定義• オブジェクト指向プログラミング• オブジェクト指向ソフトウェア工学 (分析、設計、開発、UML…)• オブジェクト指向ソフトウェア技術 (コンポーネン
ト、分散オブジェクト、…)• オブジェクト指向データベース• ここでは「言語」を中心に扱う

3

3.2

オブジェクト指向的な考え方• 人間にとって考えやすくさせてくれるならそれは「よいこと」。

□ たとえば、「温室の温度調節システム」を考える。

• 問題は「その分だけ余計な概念が増えて負担になる」
ことを差し引いてトータルで儲かっているかどうか。

window□ 現在大量に書かれている C++や Java のコード→ある意
味では「儲かっている」ことの傍証かも (盲目的にそう信じることはできないが)。
Ｃ
sensor

3.3

本節のまとめ□ 「オブジェクト指向」とは「考え方」

heater□ 「もの」と「動作」→我々の日常に近い→考えやすい→
同じ労力でより複雑なものまで考えられる□ 旧来の考え方→手続き+受動的なデータ□ オブジェクト指向言語→オブジェクト指向のための機能• 「センサーを見て、気温が下がってきたらヒーターをさまざまに追加 (次節で見て行く)を通電するが、温度が上がりすぎたらヒーターを切る」「気温が上がってきたら窓を開くが、下がってき• 色々追加したことによる複雑さと見合うかどうかが問題たら閉める」など機能中心に考える• 制御する要素や条件が複雑になるとごちゃごちゃになりやすい。

オブジェクト指向言語の基本部分

4□ オブジェクト指向だと…

□ 「最初のオブジェクト指向言語」って知っていますか?

暑いよ！

□ Simula →最初のオブジェクト指向言語

window• 開発されたのは 1960 年代半ば。最終版は Simula67
閉めていい？

• もともと「シミュレーションのための言語」→「もの」を「まねする」しくみとしてオブジェクト指向

止めて
いい？

を導入

• しかし現在のオブジェクト指向言語に見られる基本的な仕組みはすべて持っている→極めて先進的

sensor

寒いよ！

• Simula が持っていた機能 → クラス、インスタンス、
カプセル化、メソッド、メッセージ送信記法、継承、

heater

包含型、動的分配• オブジェクト指向→「気温センサ」「ヒーター」「窓• その後追加された基本概念→ インタフェース、抽象クラス、入れ子クラス、メタクラス、自己反映機能、…

開閉装置」などの「もの」を考える→「気温センサ」
は温度が低いと「ヒーター」、高いと「窓」に注意を喚起→「ヒーター」は注意を喚起されると、定期的に• この節では「基本部分」として、Simula が持ってい「センサ」に温度を尋ね、一定以下だと通電、十分暖た機能プラスインタフェースの範囲を取り上げる。例

かいならヒーターを止めて仕事を終る→人間にとっ

題は Java 言語による。

て考えやすく、適度な大きさに分けて考えられる。

4.1

□ …で。

クラスによるオブジェクト定義• 「結局、従来と同じことをやってる」と思いますか? □ オブジェクト: さまざまな「種類」がある (はず)。例:「乗用車」「トラック」「バス」• 「それは大変よさそうだ」と思いますか?
□ 1 つの種類のオブジェクト:□ 「オブジェクト指向が」よいかどうか…

「私の車」「実家の車」• プログラムの動作そのものは同じことをさまざまに書ける。当然。

4

複数ある (はず)。例:□ 「種類」を「クラス」(と呼ばれる単位) で記述し、「ク □ 動的な言語では変数の位置も探索する場合がある
ラス」を雛型にインスタンス (個々のオブジェクト) を• 多くの変数があり、一部にしか値を設定しない場合

1 つ以上生成には有利かも□ クラスに規定されるもの• 多重継承の場合にも有利 (後述)

• インスタンス変数 (状態変数とも呼ぶ) →各インスタ □ 通常、動的分配のための手当てが必要 (後述)
ンスの「状態」ないし「固有の値」を保持• 動的分配を使わないならデータ領域だけでよい (C++• メソッド (C++用語では「メンバ関数」) →各インスなど)
タンスに付随する手続き。この手続きの中では、インスタンス変数の読み書きが可能。
□ Simula の用語ではインスタンスは「永続的なブロック」、 4.3 カプセル化
インスタンス変数は「ブロックの実行が終わっても値が消滅しないようなブロックローカル変数」と位置付けて □ インスタンス変数の値は、そのインスタンスが持っていいた。しかし意味的には上記のように、現在のオブジェるメソッドの中からしか読み書きできない→カプセル化
クト指向言語と同じ。

• カプセル化によってインスタンス変数群に決まった制約を持たせ続けることができ、外部からそれを破

public class Sample21 {public static void main(String[] args) {Car c1 = new Car("My Car", 50);
Car c2 = new Car("Father’s Car", 80);
c1.showInfo(); c2.showInfo();c1.changeSpeed(-20);
c1.showInfo();}}

壊されないことが言語仕様上保障される□ たとえば:存しておけば計算効率がよくなる→ただし x や y の値が変化したら必ず再計算• 「x や y を変更したら必ず再計算」を保証することができるか?

class Car {String name;double speed;
public Car(String n, double s) {name = n; speed = s;}public void changeSpeed(double d) {speed += d;}public void showInfo() {System.out.println("Car: " + name +" speed: " + speed);}}changeX(...) { x = ○; recalcV(); }changeY(...) { y = ○; recalcV(); }useV() { return v; }relalcV() { v = 複雑な計算 (x, y); }□ 約束を変更したときにも変更が及ぶ範囲が限定される• 「v が参照されないのに再計算は無駄」→この無駄を省けるか?
changeX(...) { x = ○; vChanged = true; }changeY(...) { y = ○; vChanged = true; }useV() { if(vChanged) {recalcV(); vChanged = false; }return v; }% javac Sample21.java% java Sample21
Car: My Car speed: 50.0
Car: Father’s Car speed: 80.0
Car: My Car speed: 30.0%

□ その後のオブジェクト指向言語では、外部からインスタンス変数をアクセスできる「ようにも」指定可能に…(あまりよくないと思う)

4.4
4.2

「v = 複雑な計算 (x, y)」→ v の値を保クラスの実現方法

カプセル化の実現□ カプセル化は単なる「スコープの問題」だからコンパイ
ル時のみで処理□ クラスの実現はごく簡単→レコード型だと思って変数の割り当て等を計算すればよい。

• C や C++のように「別の型だと強制的に見なすキャス
ト (reinterpret cast)」があると問題• インスタンスの生成時にその大きさの領域をヒープから割り当てる• 動的な (弱い型の) 言語では実行時にクラス情報を参照して検査• 必要なら初期設定を行う□ インスタンスを「その場に」取るような言語ではカプセ
ル化はしてもコンパイラには利用するクラスの中が見えないとまずい。例:C++ (他にも Ada や Eiffel など)□ 変数の読み書きは領域先頭からの固定オフセットに対して行えばよい5interface Figure {public void draw(Graphics g);
public void moveTo(int x, int y);
public int getX();public int getY();}

class Human {private: ←以下は見ちゃだめ
char *name; float height, weight;
public:←以下は見ていいchar *getName();float getWeight();...
}□ こういう情報がヘッダファイルに入っている。

• つまり「見ていい」「見ちゃだめ」を区別…とはいってもそこに書いてあるという嫌らしさ• さらに、後で変数を増やすとコンパイルし直しになる。

インタフェース

4.5□ 先に出て来た「見ていい部分」だけを定義として記述したもの→オブジェクトの「外側から見える側面」→インclass Circle implements Figure {int gx, gy, rad; Color col;public Circle(int x, int y, int r, Color c) {gx = x; gy = y; rad = r; col = c;}public void draw(Graphics g) {g.setColor(col);g.fillOval(gx-rad, gy-rad, rad*2, rad*2);}
public void moveTo(int x, int y) {gx = x; gy = y;}public int getX() { return gx; }public int getY() { return gy; }}タフェース (界面)□ ちなみにこれを動かすために必要なのは…

• インタフェースを独立した記述対象とした言語→普• Sample22.java を打ち込み、コンパイルする及したものでは Java が最初□ 例:• 「javac -target 1.1 Sample22.java」← MS VM、

Figure(図) というインタフェースを考える

Netscape 4 等で動かす場合は jdk1.1 互換を指定す• 図は「画面に描ける」「X 座標/Y 座標を持ち、変更で

ること

きる」

• その同じディレクトリに以下の Sample22.html を置
き、ブラウザまたは appletviewer(JDK に付属してinterface Figure {public void draw(Graphics g);
public void moveTo(int x, int y);
public int getX();public int getY();}

くるアプレット表示ツール) でこの HTML ファイルを開く<html><head><title>sample</title></head><body><applet code="Sample22.class" width="300" height="200"></applet></body></html>□ このような「メソッドの名前、引数/返値の型の情報を合わせたもの」を「シグニチャ(signature)」と呼ぶ。

4.6• シグニチャ情報があればコンパイル時に型検査できる

動的分配□ 変 数 x に オ ブ ジェク ト が 格 納 さ れ て い る と し て 、□ 上のインタフェースを使ったアプレット

x.m(...) はメソッド m を呼び出す。

□ アプレットは「円を 2 つ描く」もの□ 変数 x がクラス A のインスタンスであれば、クラス A で□ 「円」はクラスで実現定義されているメソッド m が呼ばれる。クラス B のイン□ 「円」は Figure インタフェースに従う

スタンスであれば、クラス B で定義されているメソッド

m が呼ばれる。→どのメソッド m であるかは、実行時にimport java.applet.*;import java.awt.*;

x に格納されているオブジェクトのクラスに応じて動的に定まる→動的分配 (dynamic dispatch)

public class Sample22 extends Applet {Figure f1 = new Circle(50, 50, 30, Color.red);
Figure f2 = new Circle(80, 90, 50, Color.blue);
public void paint(Graphics g) {f1.moveTo(f1.getX()+3, f1.getY()+2);
f2.moveTo(f2.getX()-1, f2.getY()+1);
f1.draw(g); f2.draw(g);try { Thread.sleep(500); repaint();} catch(Exception e) { }}}• 「円の『描く』、矩形の『描く』、線分の『描く』はすべて実装としては別のものだが、機能としては同じに扱える」→ 1 つのコードで区別なく記述できるようにしたもの• 動的分配がないと、「if 円 then 円. 描く () elif
矩形 then 矩形. 描く () else ... 」となってしまう→コードがごちゃごちゃ、プログラマが一時に6

考えることが増える。これと対比すると、動的分配

4.7は極めて強力な機能だと言える

動的分配の実現□ 最も原理的には…

□ 先のアプレットに「正方形」を増やした• メソッドへのポインタの表を各オブジェクトに持たせる

import java.applet.*;import java.awt.*;• この表を検索してメソッドを見つけてから呼び出す

public class Sample23 extends Applet {Figure f1 = new Circle(50, 50, 30, Color.red);
Figure f2 = new Circle(80, 90, 50, Color.blue);
Figure f3 = new Square(120, 40, 40, Color.green);
public void paint(Graphics g) {f1.moveTo(f1.getX()+3, f1.getY()+2);
f2.moveTo(f2.getX()-1, f2.getY()+1);
f3.moveTo(f3.getX()+2, f3.getY()+3);
f1.draw(g); f2.draw(g); f3.draw(g);try { Thread.sleep(500); repaint();} catch(Exception e) { }}}interface Figure {public void draw(Graphics g);
public void moveTo(int x, int y);
public int getX();public int getY();}

Circle instance

Square instance

xpos

xpos

ypos

ypos

rad

len

col

col

draw

methodmoveTodraw

methodmoveTomethodgetX
getY

methodgetX
getY

methodmethodmethodmethod□ 実際にはいくつかの点で最適化• 同じクラスのオブジェクトならメソッドは同じ→メ
ソッド表はクラスごとに 1 つだけ作成し、各オブジェ
クトは自分が属するクラスを表す情報 (クラスオブ
ジェクトへのポインタ) を持つ

class Circle implements Figure {int gx, gy, rad; Color col;public Circle(int x, int y, int r, Color c) {gx = x; gy = y; rad = r; col = c;}public void draw(Graphics g) {g.setColor(col);g.fillOval(gx-rad, gy-rad, rad*2, rad*2);}
public void moveTo(int x, int y) {gx = x; gy = y;}public int getX() { return gx; }public int getY() { return gy; }}• 一定の条件が整えば表を探索しなくても「表の何番目か」はコンパイル時に分かる• 表の探索が必要な場合も、一度探索した結果をキャッ
シュしておくと次回はそれが再利用できることが多い

4.8

継承

□ 継承とは→あるクラスから別のクラスに定義を「引き継ぐ」ことclass A

class Square implements Figure {int gx, gy, len; Color col;public Square(int x, int y, int l, Color c) {gx = x; gy = y; len = l/2; col = c;}public void draw(Graphics g) {g.setColor(col);g.fillRect(gx-len, gy-len, len*2, len*2);}
public void moveTo(int x, int y) {gx = x; gy = y;}public int getX() { return gx; }public int getY() { return gy; }
}

int x
int y
metod A { ... }metod B { ... }class Bextends A
int x
int y
int z
metod A { ... }metod B { ... }metod C { ... }□ 強い型の言語の場合、変数 x に対してメソッド m が使え• 典型的には、インスタンス変数定義とメソッド定義

るかどうかは型検査でチェックされる→変数 x に実行時

(実現の継承)に何が入れられるかを規定しておく必要 (先の例で出て• 追加や差し替え (オーバライド) も可能

来たインタフェースなど。型については後でとりあげる)7• クラスにコンストラクタを書かなければ、引数なし• しかし厳密な「継承の定義」をしはじめると難しいのコンストラクタが自動的に作られる(後述)• →全体として、初期設定を必ず通る方向でチェックさ□ 継承も動的分配の対象になる (現在のところ普及していれる

るすべての言語において、親クラスはインタフェースの□ それはそうと、上のような「差分プログラミング」はど機能を兼ねている)う思う?

□ 継承は何が嬉しいか?

□ Java アプレットも継承を使った実例になっている• 類似したクラス群を少ない記述で作成できる、定義• アプレットは java.applet.Applet クラスからメソッド群を継承の共有• 新しいクラスを作るとき、違うところだけ書けばよい→差分プログラミング• 自分の領域を再描画するときはメソッド paint() を呼ぶ• 共通の親を持つクラスのオブジェクトを総称的に扱える (なぜなら同じ変数群、同じメソッド群を持つ• Applet のサブクラスで paint() を差し替えることから)(ただしそのためには動的分配も必要)

で独自の画面を作成• 強い型のオブジェクト指向言語では、子クラスの値□ このように「ある決まった部分だけオーバライドによりは親クラスの型に互換→型の問題 (後述)
差し替えて使えるような構造」→「アプリケーションフ□ たとえば先の例で Circle と Square の違うところは「ほレームワーク」
んの少し」→その「差分」だけ書いてすませることもできる。
• 実際にはもっと大きいプログラムで使われる (例:MFC)
class Circle implements Figure {int gx, gy, rad; Color col;public Circle(int x, int y, int r, Color c) {gx = x; gy = y; rad = r; col = c;}public void draw(Graphics g) {g.setColor(col);g.fillOval(gx-rad, gy-rad, rad*2, rad*2);}
public void moveTo(int x, int y) {gx = x; gy = y;}public int getX() { return gx; }public int getY() { return gy; }
}

4.9

抽象クラス

□ 抽象クラス:機能の一部を子クラスの実装に任せるよ

うなクラス• その「子クラスに任されている」メソッドを抽象メ
ソッドという• Smalltalk では、抽象メソッドは例外を投げることで示す□ Java では、抽象クラス/抽象メソッドを宣言→コンパイ
ラが検査 (冒頭に修飾子「abstract」) をつける

class Square extends Circle {int len;public Square(int x, int y, int l, Color c) {super(x, y, 0, c); len = l/2;}
public void draw(Graphics g) {g.setColor(col);g.fillRect(gx-len, gy-len, len*2, len*2);}}□ この「super(...)」とは?

□ 先の例題で「円と長方形の共通部分」を SimpleFigureという抽象クラスにくくり出してみる
import java.applet.*;import java.awt.*;
public class Sample25 extends Applet {Figure f1 = new Circle(50, 50, 30, Color.red);
Figure f2 = new Line(80, 90, 50, 70, Color.blue);
Figure f3 = new Square(120, 40, 40, Color.green);
public void paint(Graphics g) {f1.moveTo(f1.getX()+3, f1.getY()+2);
f2.moveTo(f2.getX()-1, f2.getY()+1);
f3.moveTo(f3.getX()+2, f3.getY()+3);
f1.draw(g); f2.draw(g); f3.draw(g);try { Thread.sleep(500); repaint();} catch(Exception e) { }}}→ 親クラスのコンストラ

クタを呼ぶ。

• コンストラクタを呼ばないと初期設定が行なわれないので危ないから、呼ばないと許されないことになっている。

• 引数なしのコンストラクタがあれば、自動でそれを呼んでくれる8• 円は楕円の特殊な場合である

interface Figure {public void draw(Graphics g);
public void moveTo(int x, int y);
public int getX();public int getY();}• 円よりも楕円の方が (長径と短径があるので) 機能は多い□ 「円」が使われるところすべてに「楕円」をあてはめて
よいか?

abstract class SimpleFigure implements Figure {int gx, gy; Color col;public SimpleFigure(int x, int y, Color c) {gx = x; gy = y; col = c;}public abstract void draw(Graphics g);
public void moveTo(int x, int y) {gx = x; gy = y;}public int getX() { return gx; }public int getY() { return gy; }}それが YES でないようなアプリケーションなら、独立したクラスにした方がよい。

• YES であれば、「円」に「縦横比率」を追加したもの
が楕円、という位置付けで楕円をサブクラスにすることはあっていいかも• 結局、アプリケーション (やライブラリの使用想定
場面) 次第4.11

class Circle extends SimpleFigure {int rad;public Circle(int x, int y, int r, Color c) {super(x, y, c); rad = r;}public void draw(Graphics g) {g.setColor(col);g.fillOval(gx-rad, gy-rad, rad*2, rad*2);}}

本節のまとめ□ オブジェクト指向言語の基本部分→ Simula にほとんどある (一部ないものもある)• クラス、インスタンス• コンストラクタ→初期設定• インタフェース→オブジェクトの「切り口」「使い方」• 動的分配→オブジェクト指向言語の特に重要な機能• 継承→差分プログラミング、フレームワーク、(インclass Square extends SimpleFigure {int len;public Square(int x, int y, int l, Color c) {super(x, y, c); len = l/2;}
public void draw(Graphics g) {g.setColor(col);g.fillRect(gx-len, gy-len, len*2, len*2);}}

タフェースを兼ねる)• 抽象クラス→クラス階層の構造化□ ここまでで課題の 1 番目はできるように配慮しました。

5

Smalltalk: 中興の祖

□ Smalltalk システム:

Alto システム上の言語処理系+

実行環境
class Line extends SimpleFigure {int dx, dy;
public Line(int x1, int y1, int x2, int y2, Color c) { • Alto: ゼロックス社の Palo Alto 研究所で開発さsuper(x1, y1, c); dx = x2-x1; dy = y2-y1;
れた「世界最初の」「パーソナルワークステーション」}• Smalltalk にはいくつかバージョンがあるが、1980
public void draw(Graphics g) {g.setColor(col);年の Smalltalk-80 が一応完成された版→その後もg.drawLine(gx, gy, gx+dx, gy+dy);
少しずつ改良}}• Alan Key らが Dynabook 構想の実現の一歩として開
発した

4.10

• ビットマップディスプレイ、対話的グラフィクス、サ

クラス階層の設計

ウンド、ウィンドウシステムなど、当時の水準から□ (抽象クラスや継承を含めた) よいクラス階層のデザイ

見ると極めて先進的なシステム

ン→なかなか難しい重要な部分□ 指針としては「抽象的なもの (上位概念) →親クラス」と言われているが…
□ 「円」と「楕円」はどっちが親クラス?

□ オブジェクト指向によるプログラミングの容易さがあってはじめて可能だった、とされている□ 「中興の祖」という意味→ Simula にはじまるオブジェ

またはどちら

クト指向言語について、世の中に再認識させた

でもない?

9

• プログラミング言語屋にとっては「センセーション」 5.3
だった□ コードブロック:□ Smalltalk の「見ため」の多くは Apple の Lisa →コードの断片だが、それ自身オブジェ

クト。他の言語でいえば「クロージャ」に相当する

Macintosh に引き継がれた (Smalltak 言語は引き継が• メッセージ valu(引数つきの場合は「value: 引数」
等) を送ると、そのコード内容を実行して return 文で指定した値を返すれなかった)□ Smalltalk 言語は製品としてずっと存在し続けたが開発

z ← [x ← x + 1. ↑ x] value.
z ← [:n | x ← x + n. ↑ x] value: 100.

言語としてはマイナーであり続けた。有償だったし。
□ 1997 年、オリジナル Smalltalk の開発者たちが再• ブロックはブロックの周囲の環境をアクセスできる(だからクロージャ)

結集して開発したフリーの処理系 Squeak が公開に(http://www.squeak.org/)

5.1

コードブロックの多用• 一方で、副作用だらけになるという問題点も□ Java では、コードブロックは無いがその代り「内部ク「純粋な」オブジェクト指向言語

ラス」や「無名の内部クラス」が使える (後述)

□ 「純粋」という意味→すべてがオブジェクトである。たとえば整数や文字や論理値も (C++、Java 等ではこれら

5.4は基本型でありオブジェクトではない)• そのため、極めて統一的な言語仕様とできる (すべてのもののふるまいはサブクラスを作って改良可能)

制御構造□ 制御構造もブロックとメソッドで構成(x > 10) ifTrue: [x ← x - 1] ifFalse: [ ... ].
[x > 10] whileTrue: [....].

• たとえば「整数」のふるまいを変えたものも作れる• ただしリテラルがもとの Integer クラスのものな• なぜ上は「(...)」で下は「[...]」なのか分かりま

ので…

すか?

• コンパイラを変更してしまえば変えられる□ そのほか「このような値が見つかるまで探す」といった指定にもブロックを利用→ Lisp 系の言語に近い (記号5.2

特徴的な構文型もある)□ すべては「メッセージ送信式」(と変数代入)

5.5• キーワードセレクタ型:
オブジェクト セレクタ.
valu ← aPoint x.
オブジェクト セレクタ: 引数 セレクタ: 引数 ….
anArray at: 10 put: x.

先進的なプログラミング環境□ ウィンドウシステムがほとんど普及していない時期から
ウィンドウ環境だった□ クラスブラウザ、バックトレーサ、デバッガなどが組み• 演算子セレクタ型:

込まれた統合プログラミング環境だった

オブジェクト 演算子 オブジェクト.
x ← x + 1.

• ソースを追加/修正すると環境全体が変化していしまう→環境全体のダンプを取って保存 (もちろんソー□ 「メッセージ送信」とは要するにメソッド呼び出しのこと

ス単独でも保存とロードはできたが)• 全体的に言語、環境とも Lispっぽいと言える。cf.

• オブジェクトが指定されたセレクタに対応するメソッ

InterLisp-D

ドを持たないときは、messagenotunderstood というセレクタのメッセージに変換されて送り直される(このメソッドは Object クラスで定義されている)→自前でエラー処理したければこれをオーバライド

5.6

MVC フレームワーク• 並列性や分散性は Smalltalk にはない (注: スレッ □ 画面に見える「もの」(ウィンドウの内容や部品) を M/V/Cに分けて構築ドはあるが並列実行ではない)。この面で拡張を行う
研究は多数あり → Concurrent Smalltak、その他

10• Model: 「もの」の状態を表すオブジェクト。たとえばスライドレバーであれば「現在の値」

• View: 「もの」の状態を表示するオブジェクト。た6.1

とえばスライドレバーであれば、「レバーの絵」やFlavors□ Zetalisp (Lisp Machine System で採用した Lisp の「数値表示窓」

方言) 上のオブジェクト指向機能。クラスのことを• Controller: 「もの」を操作するための動作を提供するオブジェクト。たとえばスライドレバーであflavor と呼ぶ• (defflavor フレーバ名 各種情報…) で「クラス」
を定義

れば「レバーをドラグする」「プラス/マイナス押しボタン」。

• flavor のインスタンス→オブジェクト□ 1 つのモデルに対してビュー、コントローラは複数あっ• (send オブジェクト セレクタ 引数…) → メッセー
ジ送信てよい (上の例)□ その後の多くのグラフィカルなシステムにおいて MVC フ
レームワークが採用された□ ここまでのところは Smalltalk と本質的におなじ• View と Controller を分離する必要はどれくらいあるか? Java 2(Swing) などではこれらを一体化し6.2

た delegate というものを使用• モデルを分離する、という考え方はいずれにせよと

多重継承□ Flavors による重要な拡張の 1 つ。flavor には複数の
親 flavor が指定できる→多重継承ても有効 (後述)• 多重継承では小クラスは親クラスすべてからインス

5.7

タンス変数、メソッド群を引き継ぐ→「混ぜる」こ

本節のまとめとによる干渉もあり使い方は難しい□ Smalltalk → オブジェクト指向の強力さを世に知らし□ 実際には、「通常の (インスタンスを作る) クラス」と、めた「他のクラスにまぜて機能を追加するクラス」(mixin クラス) を区別して使い分けることが通例• 「純粋な」オブジェクト指向言語 (整数等もオブジェ
クト)

• 例:

• コードの集まり (ブロック) もオブジェクト

クラス」などを混ぜて機能の増えたウィンドウを作っ• 制御構造はブロック等のメソッドて行く• このような操作を mixin 操作と呼ぶ□ MVC フレームワーク→パターンの元祖

6

ウィンドウクラスに対し、「窓枠をつける mixin

6.3

Lisp 系のオブジェクト指向言語□ Smalltalk は最初から Lisp によく似た側面を備えてメソッド結合□ Flavors で提案されたもう 1 つの重要な拡張。

• Smalltalk では子クラスのメソッドは親クラスの同いた• そのため、Lisp 屋は Lisp にオブジェクト指向を導入することで Smalltalk のようなよい言語/環境を入手できるのではと考えた

名メソッドを置き換え→親クラスのメソッドの動作「も」利用したい場合は「super セレクタ …」により明示的に呼び出し• 多重継承では親が複数あるから上の方法ではいまいち• 実際、多くの Lisp ベースのオブジェクト指向言語が• C++では「どの親の同名メソッド」という形で呼べる作られたが、この方法で十分かどうかは?
• その際、Smalltalk や Simula になかった新しい概□ Flavors では、通常のメソッド (primary) のほかに、念も多く考案された
deamon メソッド (before daemon、after daemon) が□ 現在でも標準として残っているのは CLOS(Common Lisp
ある (実際にはもっとあるがこれらが主に使われる)
Object System) →ただし今後の Lisp はどれもオブジェ□ 多重継承とメソッドのオーバライドがある状態では…、クト指向機能は持つようになる (CLOS 方式かどうかは次の順でメソッド群が呼ばれる
分からないが)• まず、before daemon が親クラスから子クラスへの順で呼ばれる11• primary method はこれまで通りのオーバライドな□ Simula は強い型の言語だったが、その後、Smalltalk、

ばれる

Flavors、等はすべて弱い型の言語• 最後に after daemon が子クラスから親クラスへの順で呼ばれる□ 何のためにこうなっている?

オブジェクト指向と型

7ので一番最近に定義された子クラスのものだけが呼□ C 言 語 に オ ブ ジェク ト 指 向 を → や は り「 オ ブ ジェク
ト型」はすべて一緒、というタイプが多かった (例:→ before daemon は「前しまつ」、after daemon は「後しまつ」を行い、それらは順番に結合されて各レベルのクラスの仕事を実行する

Objective-C) →強い型ではない□ 10 年以上たって、ようやく「強い型のオブジェクト指向言語」が当たり前になった (C++が代表的)□ 使いこなせば便利なのかも知れないが、やっぱり難しい

(と思う)

7.1
6.4

強い型の概念と利点/弱点□ 強い型とは?

CLOS とマルチメソッド方式→ コンパイル時にすべての式や変数の型が定まっている□ CLOS (Common Lisp Object System) → CommonLispの言語仕様のうちの、オブジェクト指向機能部分をいう• 利点:

コンパイル時検査、設計の手段(後から追加されたもの)• 弱点:

繁雑、めんどくさい???

□ 中庸もある:□ 最大の変化→汎用関数 (generic function) に基づく
メソッド呼び出し• Flavors:

例 CommonLisp → 型はなくてもいいけ

ど、指定してもいい。指定すると効率がよいかも/コン
パイル時検査が可能(send オブジェクト セレクタ …) →「どのメソッドか」は「オブジェクト」と「セレクタ」
で決まっていた
7.2 弱い型のオブジェクト指向言語• 最初の引数 (レシーバ) のみを重視しすぎ? → 「す□ 変数にも式にもコンパイル時の型はない
べての引数がメソッドの決定に関与する」□ しかし、実行時には型 (==クラス) がある(defclass X ....)(defclass Y ....)(defmethod method1 ((a X) (b Y)) ... *1 )(defmethod method1 ((a X) (b X)) ... *2 )...
(method1 anX anY) → *1 が呼ばれる(method1 anX anX) → *2 が呼ばれる• 「anObject message.」→「OK である」か「そのメソッドはない!」かどちらか。
• 「そのメソッドはない」がどこで起こり得るかを予め知る方法はない→製品としてソフトを作るときには弱点となり得る• 「メソッドがクラスに付属していない」「構文的には• 「OK である」ならよいのか? → たまたまそういう名前のセレクタが利用可能、だったらもっとたちが普通の関数みたいな見え方」→特徴的 (好みも分か
れる)

悪い?

• 多重継承やメソッド結合は Flavors 以来引き継がれ
ている

7.3
6.5

本節のまとめ強い型のオブジェクト指向言語□ Simula が既にそうであった□ Lisp 系のオブジェクト指向言語→メジャーではないが様々な試み• 「型」と「クラス」は同じものとみなす (ちょっとは違うが)• 多重継承• 「ある型の変数/式」は実行時に「そのサブクラスの値も持つことができる」• メソッド結合• メタオブジェクトプロトコル (MOP)• マルチメソッド□ この規則は通常の「強い型の言語」からはだいぶ離れている
Pascal ... x:integer := 1;x の型:integer、1 の型:integer
Java ... o:Object := new Integer(1);o の型:Object、式の型: Integer(Object のサブクラス)

12• Object 型には任意のオブジェクトが入れられてし
まう

継承と委譲

8

□ 継承 (inheritance) → Simula、Smalltalk 以来の「由• 代入の左辺と右辺の型は同じでなく包含関係→複雑

緒正しい」やりかたさの原因• ただし、メソッドを呼ぶときは型が合わないとだめ• 使っているうちに、色々問題もあることが分かって

i:int := o.intValue(); ... ×
i:int := ((Integer)o).intValue(); ... ○来た□ 委譲 (delegation) →継承の代替として使えるメカニ• このキャストは「実行時の型検査を伴うキャスト」でズム

あって C のキャストとは違う (もともとのオブジェク
トが Integer ないしそのサブクラスでなければ例外• 複数オブジェクトの組合せ (composition) と相性がよい→継承よりも委譲を使うことが増えているが発生)□ ある型に属するかどうかを判定→ instanceof 演算子

8.1

if(o instanceof Integer) ...

継承の意味づけ□ 継承がやってることは何かというと…

□ もっと自由に型の情報そのものを扱う→自己反映機能• インスタンス変数とメソッドを引き継ぐ(後述)• その結果として、呼べるメソッドの集合や外から見7.4

たオブジェクトの振る舞いを引き継ぐ

インタフェースと型• たとえば B が A のサブクラスであれば、「A のインス

□ 単一継承でサブクラス階層だけだと型の包含関係は非常

タンス」の代りに「B のインスタンス」を与えてもそにシンプルのまま動く (建前としては)• 動的分配の前提として、「A 型の変数に A のサブクラ□ 多重継承があると 1 つのインスタンスが複数の親の型にスがいろいろ入れられる」ということが必要含まれることになるが、まだ包含関係は明らか□ インタフェースではそれを実装しているすべての型を「くくる」という点で不規則な構造が作れてしまう (た□ しかしよく考えると、これは「実装の継承」が先にあり、その結果として「たまたまどれでも同じように取り扱えだしループは許さない)る」ようになっているだけとも思える。この「たまたま」は気持ち悪い• インタフェース自体にも包含関係があるのでさらに面倒なことに…

8.2
7.5

本節のまとめ継承の実装□ ごく素直な継承の実装方法→オブジェクトの構造が重要。

□ 強い型はコンパイラによる誤り検査や設計の手段として
有用

インスタンス変数を定義された順に並べておく→子クラスでインスタンス変数を追加した場合は後ろにつけ加えていく□ 「お仕事の言語」では強い型のものが主流□ オブジェクト指向以前の「強い型」とオブジェクト指向の「強い型」はやや違っている (型の包含関係)• より広い型の変数に狭い型の値 (インスタンスを入れられる)• 広い型から狭い型に戻すことができる (実行時チェッ
クが必要)

13

u

class U

x

int x
int y

• 共通の親があったとき 2 重にコピーする→その場所
は親クラスとしてそのまま扱える (ただしポインタ
逆変換の問題がある)• 共通の親は統合する→「どこに親が埋まっているか」

metod A { ... }metod B { ... }yのポインタをそれぞれのインスタンスに埋めることwで対応
class W

x

int x
int y
int t
metod A { ... }metod B { ... }metod D { ... }ytvclass V

□ 「共通の親は統合」でもう 1 つの素直なアプローチ:

int x
int y
int z
metod A { ... }metod B { ... }metod C { ... }最初に名前で探索し、その場所を覚える

8.5

継承の問題点□ 継承にはさまざまな問題点があった→何が問題か、分か

x

ります?

y

□ 実装と界面の混同

z• 継承は実装を引き継ぐことで、インタフェースを共• この方法であれば、クラス A のどのサブクラスでもA までで定義されている変数のオフセットは同一 →通にすること、多相性 (polymorphism、動的分配)を提供することはまた別の問題だが混同されがち

オフセットで直接アクセス可能• Java のようにインタフェースを別にすることが必要• メソッドのコードがそのまま利用可能(ただし Java でも継承すると多相性がついてくるので中途半端)• 分かりやすく、効率がよい。ただし多重継承に対応できない□ カプセル化の破壊

8.3• サブクラスを作ると、その中では親クラスの変数がメソッド探索自由にいじれてしまう→カプセル化の破壊□ メソッド呼び出しで実際にどのメソッドが動くかはオブ• C++、Java など→サブクラスでいじれる変数、いじジェクトのクラスによって変化→メソッド探索

れない変数を区別可能に (private →サブクラスで• Smalltalk では、最初にそういう呼び出しがあったもいじれない変数、protected →サブクラスでもいときに探索を行い、その情報をキャッシュに保持。ク

じれる変数) →それが問題の解決になってるのかど

ラス構造が変化したときはキャッシュをご破算にしてうか??

やりなおす。動的にクラスが変化する環境ならでは□ 機能追加の制約• C++、Java などのコンパイルする言語では、分岐表• 単一継承の場合、1 つずつしか機能を追加して行け(メソッドテーブル) を作ってそれに基づいて分岐すればよい。分岐表そのもののスロットも変数と同様

ない→たとえば図形に「動く機能」「大きさが変わる機能」を追加したいとすると、それぞれの機能のあにして管理可能るなしごとにクラスを分けることに→機能が N 種類

• インタフェースの場合は 1 つのクラスがさまざまな
インタフェースを実装しているので面倒。Java では

あったら 2 の N 乗必要→組合せ爆発• 多重継承があれば「動く機能」「大きさが変わる機能」などをそれぞれ別のクラスにして「混ぜる」ことで呼び出し時に探索しているが、工夫次第では「表引き」にもできる

必要なクラスが作れる。ただし混ぜたものの干渉が

8.4

心配

多重継承の実装□ 多重継承:• 問題:□ 継承は静的

2 つの親から継承すること• 実行時に機能を追加したり外したりといったことは複数の親が共通の親クラスを持っていたらど

できない。

うするか?
□ C++の多重継承では 2 つの方式がサポートされているの• しかし場合によっては実行時に機能の調整を行ないたい。たとえば「動かない円」を作っておいたがそれを途中で動くようにしたい等。

で大変

14

□ 例:

GUI 部品と動作• たとえば「ボタン部品」を考えてみる。ボタンにはimport java.applet.*;import java.awt.*;
import java.awt.event.*;//← import 追加!

「ボタンを押した時の動作」があるはず。その動作はpublic class Sample26 extends Applet {Figure f1 = new Circle(50, 50, 30, Color.red);
Button b1 = new Button("B1"); // ボタン• しかし汎用の Button クラスには当然、アプリケー
Button b2 = new Button("B2"); // ボタン
ション固有の動作は入っていない
public void init() { //←初期設定メソッド
setLayout(null);//←自動配置 off• ではどうするか? → 継承を使って、「動作」メソッadd(b1); b1.setBounds(10, 10, 60, 30); //配置
ドをオーバライドして、そのメソッド中でアプリケー
add(b2); b2.setBounds(10, 50, 60, 30); //配置
b1.addActionListener(new MyAdapter1(this, f1));
ション固有の動作を行なわせる
b2.addActionListener(new MyAdapter2(this, f1));• Java は JDK 1.0.x ではそうしていたが、JDK 1.1} // ↑ボタンにアダプタを設定
からはやめている→動作 1 つごとにサブクラスを作
public void paint(Graphics g) { f1.draw(g); }るのが煩雑、複数の場所 (例: メニュー項目やキー }

アプリケーション固有

ボードショートカット) から同じ動作を起動するときの共有ができない、実行時に割り当てを変更できない

委譲 (delegation)

8.6□ 継承は「親のインスタンス変数やコードを取り込んで来て自分の一部として実行させる」→カプセル化が壊れる
等の問題□ 別オブジェクトの機能が必要なら、それを別のインスタンスとして持っていて、これを普通に呼び出すことでも利用可能→委譲の考え方• 簡単に言えば、自分で実装しないメッセージを「たらいまわし」にする• カプセル化が壊れない• 委譲先を実行時に動的に切り替えることができる• 多重継承の実装が容易 (多重継承の実装として、一部に委譲を使うことも)GUI 部品の「ボタン」に動作をつける場合• JDK 1.1 以降では各ボタンは次のメソッドを持つ
public void addActionListener(ActionListener l)• ActionListener は次のようなインタフェース
public class ActionListener {public void actionPerformed(ActionEvent e);}• 各ボタンの動作は ActionListener インタフェースを実装するクラスのインスタンス (アダプタオブジェクト) のメソッド actionPerformed() として用意• ボタンは押されるとアダプタオブジェクトの上記メ
ソッドを呼び出す□ 例題:

図形を動かすボタンをつけてみる

abstract class SimpleFigure implements Figure {int gx, gy; Color col;public SimpleFigure(int x, int y, Color c) {gx = x; gy = y; col = c;}public abstract void draw(Graphics g);
public void moveTo(int x, int y) {gx = x; gy = y;}public int getX() { return gx; }public int getY() { return gy; }}class Circle extends SimpleFigure {int rad;public Circle(int x, int y, int r, Color c) {super(x, y, c); rad = r;}public void draw(Graphics g) {g.setColor(col);g.fillOval(gx-rad, gy-rad, rad*2, rad*2);}}□ 委譲のさまざまな利点□ 例:interface Figure {public void draw(Graphics g);
public void moveTo(int x, int y);
public int getX();public int getY();}

class MyAdapter1 implements ActionListener {Applet app; Figure fig;
public MyAdapter1(Applet a, Figure f) {app = a; fig = f;}public void actionPerformed(ActionEvent e) {fig.moveTo(fig.getX()+5, fig.getY()+5);
app.repaint();}}class MyAdapter2 implements ActionListener {Applet app; Figure fig;
public MyAdapter2(Applet a, Figure f) {app = a; fig = f;}public void actionPerformed(ActionEvent e) {fig.moveTo(fig.getX()-5, fig.getY()-5);15

Java の入れ子クラスと内部クラス

9

app.repaint();}}□ 入れ子クラス (クラスの中にクラス) については既に説明したが、再度整理しておく□ アダプタオブジェクトは「オブジェクト」なので、その中に「呼ばれた」時に必要なデータを持っておくことが □ 入れ子クラスの特別な場合である「内部クラス」についても説明できる□ 上の例では 2 つのアダプタクラスはほとんど一緒→ 1 つで済ませる方がスマート。だが、複数あってよいという

9.1

例のつもりで 2 つ用意した入れ子クラス□ クラスの中に別のクラスを書けるようにしたオブジェク
ト指向言語はまだあまり多くない。

8.7

Self: プロトタイプ方式のオブジェクト指向言語• クラスはモジュールのようなもので、ある程度の完結性があるから、あるクラスの中に別のクラスを入□ ここまでは「委譲」をコーディング上の手法として考えて来たが、言語機構として継承の代わりに委譲のみを使う言語も→ Self

れると言うことはあんまり必要がないと思っていた?
□ しかし Java ではクラスの中に「そのクラスだけが使う
下請けクラス」が書ける→入れ子クラス□ クラスがなく、「ひな型」のオブジェクトを複数コピー• これは、Java が「必要があればどんどんクラスを作っすることでインスタンスを作るて使う」方向だから← Smalltalk などもそういう文• 委譲した場合でも「元のオブジェクト (プロトタイプ)」を覚えておいて、自分自身に対してメッセージを送った場合元から探す。これがないと次のようなメソッド (抽象メソッド) が使えない化だが、クラスだらけでごちゃごちゃになりがち• Java ではパッケージ (階層的な名前を持つ) →クラス→入れ子クラスという 3 段階だからだいぶいい。

moveRight | n |self turn 90.
self forward n.

入れ子クラスの中にさらにクラスを入れることもできる (まず見かけないが)□ Self 言語は「プロトタイプ方式の」「委譲に基づく」オブ
ジェクト指向言語が有用だという実証の意味が大。コードの性能も動的コンパイル (よく使う/高速な組合せだ
けをコンパイルしていく) などの技術により優れていた

9.2□ インタフェースを使ってアダプタクラスを作るような場合:□ もう 1 つの (極めて普及している) プロトタイプ方式のオ• 小さいクラスが多数できてしまうので面倒→これにブジェクト指向言語→ JavaScript(次回に取り上げる)

8.8

Java の内部クラス

対しては入れ子クラスを使えばよい• アダプタクラスが保持するデータはインスタンス変数として保持する必要→その宣言や初期設定等が結

本節のまとめ構面倒□ 継承はオブジェクト指向言語の大きな特徴として注目• そこでどう考えたか…
• もっとも「継承のないオブジェクト指向言語」も可□ 2 種類のメソッド
能だしあるが• static のついたメソッド (クラスメソッド) →イン
スタンス変数にはアクセスできない、独立した関数□ 委譲 (たらいまわし) は最初は「単なる別のオブジェク
トの呼び出し」だったが、次第に継承に代わる機構としのようなものて認識• static のつかないメソッド (インスタンスメソッド)□ オブジェクト指向プログラミング全体が継承指向から委→インスタンスに付随していて、インスタンス変数

譲指向に変化を読み書きでき、他のインスタンスメソッドをそのまま呼び出せる□ これにならって、入れ子クラスも 2 種類にする!

16• static のついた入れ子クラス→インスタンス変数に □ 1 個所でしか参照しないようなアダプタクラスの名前をいちいち考えるのは嬉しくない

はアクセスできない、独立したクラスと同様• static のつかない入れ子クラス→*外側の*クラスの □ このような場合には、なおかつ「そのクラスが 1 つのク
インスタンス変数にアクセスでき、外側クラスのイラスを extends しているか、または 1 つのインタフェー
ンスタンスメソッドをそのまま呼び出せる→つまり、スを implements しているだけであれば」名前を省略で外側クラスのインスタンスを「覚えて」いる→その代

きる→無名クラス
わり new で作り出せるのはインスタンスメソッドや □ 具体的には次のような形
コンストラクタの内側→「内部クラス」と名付けた
public class X {... new Y(); ... ←ここでだけ使用• さらに内部クラスの中からは、インスタンス変数だ
けでなく final 指定の引数や局所変数 (つまり値がclass Y implements I {内部クラスの定義}

変更されない変数) も参照できる←ということは必要なら「メソッドの途中」にも書けるわけ}• これらの機能のため、内部クラスを使うといちいち
アダプタクラスにインスタンス変数を持たせなくて □ このとき、使用しているところに Y の定義本体を直接埋め込んでしまうことで名前を書かなくするもよい場合が多い→記述が簡単□ これを使って先の例を書き換えてみた

public class X {... new I() {内部クラスの定義} ...
}import java.applet.*;import java.awt.*;
import java.awt.event.*;
public class Sample27 extends Applet {Figure f1 = new Circle(50, 50, 30, Color.red);
Button b1 = new Button("B1");
Button b2 = new Button("B2");
public void init() {setLayout(null);add(b1); b1.setBounds(10, 10, 60, 30);add(b2); b2.setBounds(10, 50, 60, 30);
b1.addActionListener(new MyAdapter1());
b2.addActionListener(new MyAdapter2());}
public void paint(Graphics g) { f1.draw(g); }□ 先の例を無名内部クラスを使うように直すと:import java.applet.*;import java.awt.*;
import java.awt.event.*;class MyAdapter1 implements ActionListener {public void actionPerformed(ActionEvent e) {f1.moveTo(f1.getX()+5, f1.getY()+5);
repaint();}}class MyAdapter2 implements ActionListener {public void actionPerformed(ActionEvent e) {f1.moveTo(f1.getX()-5, f1.getY()-5);
repaint();}}}(以下同じにつき略)□ 外側クラスのインスタンス変数に直接アクセス→そのためコンストラクタも変数も不要→さっきよりずっとコン
パクトに

9.3

public class Sample28 extends Applet {Figure f1 = new Circle(50, 50, 30, Color.red);
Button b1 = new Button("B1");
Button b2 = new Button("B2");
public void init() {setLayout(null);add(b1); b1.setBounds(10, 10, 60, 30);add(b2); b2.setBounds(10, 50, 60, 30);
b1.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {f1.moveTo(f1.getX()+5, f1.getY()+5);
repaint();}});
b2.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {f1.moveTo(f1.getX()-5, f1.getY()-5);
repaint();}});}
public void paint(Graphics g) { f1.draw(g); }}(以下同じにつき略)□ ボタン以外の GUI 部品を使った例も挙げておこう。

• Button →ボタン

無名クラス

• Label →表示ラベル□ 上の例では MyAdapter1 等の名前を使う個所はそれぞれ

1 個所ずつしかない• TextField →入力欄
• Choice →選択メニュー17□ 選択メニューについては、選択肢は add() で別途追加

自己反映機能10する□ 自己反映 (reflection):

実行中のコードが、実行系

import java.applet.*;import java.awt.*;
import java.awt.event.*;の情報にアクセスしたり、実行系の状態/動作を変更しpublic class Sample29 extends Applet {Label l0 = new Label("");
Choice c0 = new Choice();
Button b0 = new Button("Calc");
TextField t0 = new TextField("");
public void init() {setLayout(null);add(l0); l0.setBounds(10, 10, 280, 30);add(c0); c0.setBounds(10, 50, 60, 30);c0.add("F to C"); c0.add("C to F");
add(b0); b0.setBounds(110, 50, 60, 30);add(t0); t0.setBounds(10, 90, 120, 30);b0.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {try {float x =(new Float(t0.getText())).floatValue();float y = (c0.getSelectedIndex() == 0) ?
(5.0f/9.0f * (x - 32.0f)) :(9.0f/5.0f * x + 32.0f);
l0.setText("Result: " + y);} catch(Exception ex) {l0.setText(ex.toString());
}
}
});
}
}

• 狭い意味では前者のみ (後者を reification と呼んで区別することも)たりできるような機能□ 何のためにそんなことをするのか?

• 例: デバッガ→実行系の内部状態を調べたり変更する必要• 例: システムの拡張→「任意の手続き呼び出しを遠隔メッセージに変換」など
• 例:

10.1

拡張可能言語 (構文や意味づけ等)

3-Lisp: リフレクションの元祖□ ベースレベルとメタレベルを区別• ベースレベル:• メタレベル:

通常の実行
ベースレベルの実行系の情報 (バインディング、継続等) が見える

• メタレベルを変更→ベースレベルでの対応する状態
変化が起こっている→これにより、言語セマンティ
クス (実行順序の制御等) が拡張可能□ 記述が短く簡潔になるという点は便利だが、最初はちょっ□ 3-Lisp のさらに特徴→「メタレベル」はさらに「メタメタレベル」によって制御可能→無限の reflectiveと分かりづらいtower になっている□ 従来の言語で「クロージャ」と呼ばれる機能を持つもの• 実装上は「遡られたところまで自動的にメタレベル
を用意」

がある

• クロージャ = 関数 + 環境
• Smalltalk のブロックも一種のクロージャ

10.2• そのクラス版が Java の内部クラスだと考えられる

Smalltalk のクラスとメタクラス□ Smalltalk では「クラス」もまたオブジェクト

9.4• クラスに対してメッセージを送る→メソッドを追

本節のまとめ加したり修正したり等ができる (実行環境全体が□ Java の入れ子クラス→クラスの中に下請けクラスが書

Smalltalk で書かれているので当然といえば当然)ける□ 内部クラス→外側インスタンスの情報や、外側メソッド□ クラスオブジェクトは Metaclass というクラスのインの局所変数などに中から直接アクセスできる→クロー

スタンス。たとえばクラス Collection のクラスオブ
ジェクトは Collection class(という式でアクセス)。

ジャ機能を実現したものと言える• うまく使えば記述がコンパクトになるが、一方でどこで何をアクセスしているか分かりにくくなる恐れもあるCollection class は Metaclass というクラスのインスタンス• Metaclass は各クラスオブジェクトを初期設定する機能をおもに提供→ Metaclass を修正すると、Smalltalk システム全体の動作が変化させられる

18

10.3

メタオブジェクトプロトコル (MOP)□ たとえば、任意のクラスを 1 つもってきてオブジェクトを生成しメソッドを呼ぶ (ただし引数はすべて空) というプログラム□ 「オブジェクト」を統括する (ふるまいを定義する) オ
ブジェクト→「メタオブジェクト」(例:

クラスに対しimport java.io.*;import java.lang.reflect.*;てはメタクラス)□ メタオブジェクトが提供するサービス、API →メタオブ

public class Sample2a {public static void main(String args[]) {BufferedReader br =new BufferedReader(
new InputStreamReader(System.in));while(true) {try {System.out.print("Class Name? ");System.out.flush();String cname = br.readLine();if(cname.equals("")) break;Class cls = Class.forName(cname);
Constructor[] cons = cls.getConstructors();for(int i = 0; i < cons.length; ++i)
System.out.println(""+i+": "+cons[i]);System.out.print("Constructor Number? ");System.out.flush();int cno = Integer.parseInt(br.readLine());
Object obj =cons[cno].newInstance(new Object[]{});
Method[] meths = cls.getMethods();for(int i = 0; i < meths.length; ++i)
System.out.println(""+i+": "+meths[i]);System.out.print("Method Number? ");System.out.flush();int mno = Integer.parseInt(br.readLine());
Object res =meths[mno].invoke(obj, new Object[]{});
System.out.println("Result Class:"+(res.getClass()));
System.out.println("Result: "+res);} catch(Exception e) { e.printStackTrace(); }}}}ジェクトプロトコル□ 最近の多くの言語ではメタオブジェクトプロトコルを提供することで自己反映機能をさまざまに利用可能• CLOS: メソッド呼び出しの意味づけなどを自由に変
更可能• OpenC++:

コンパイル時 MOP →メタオブジェクトを定義すると、コンパイル時にメタオブジェクトがソースを変更した上でコンパイル→言語の意味づけが変化させられる

10.4

Java の自己反映機能□ Java の自己反映機能→処理系そのもを変更する、という部分はない。

• 内部の状態をのぞく• のぞいた情報を利用して、その場でメソッド呼び出し等を組み立てて実行させられる□ 強い型の言語は「型が合わなければ扱えない」→リフレ
クションのような自由自在なことは表しにくい• Java ではこれらをきちんと型を割り当てた上で可能にしている• ある意味では、Lisp 等の「eval」(任意のプログラムを合成してその場で走らせる」を強い型の言語上 □ これをたとえば次のクラスに対して使ってみる…
で可能にしたといえる
public class Sample2aTest {int val;public Sample26Test() { val = 1; }public Sample26Test(int i) { val = i; }public Sample26Test add() {return new Sample26Test(val+1);}
public Sample26Test sub() {return new Sample26Test(val-1);}
public String toString() {return "Sample26Test("+val+")";}}□ 任意のオブジェクトは getClass() でその Class オブ
ジェクトを取得できる• または、static メソッド Class.forName("...") でも□ Class オブジェクトはそのクラスに関する情報を取得するメソッドを持つ• 例:

getConstractors(), getMethods(),

getMembers()□ Constructor オブジェクトの newInstance() を呼ぶと □ なお、この方法で通常取れるのは public なものだけ (セキュリティ上の制約)
オブジェクトが生成される□ Method オブジェクトの invoke() を呼ぶとメソッドが
実行できる

10.5

自己反映機能とコード生成ツール□ 例:

コンポーネントツールや GUI ビルダ

19• 画面で直接部品を配置したり動かしてみたりしたい

c0.setBounds(x0, y0, width, height);}• 動かせるためには「部品そのもの」を操作したい}});
b2.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {try {Method m = meths[maps[c2.getSelectedIndex()]];
m.invoke(c0, new Object[]{t2.getText()});
t2.setText("");} catch(Exception ex) {l1.setText(ex.toString());}}});
addMouseListener(new MouseAdapter() {public void mousePressed(MouseEvent e) {x1 = x2 = e.getX(); y1 = y2 = e.getY();
calcbox(); repaint();}
public void mouseReleased(MouseEvent e) {x2 = e.getX(); y2 = e.getY();
calcbox(); repaint();}});
addMouseMotionListener(new MouseMotionAdapter() {public void mouseDragged(MouseEvent e) {x2 = e.getX(); y2 = e.getY();
calcbox(); repaint();}});• しかし、部品オブジェクトは部品ごとに「使い方」が異なる• →自己反映機能を用いて調べつつ「呼び出せば」よい□ Java による GUI 部品配置の例
import java.applet.*;import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.*;
public class Sample2b extends Applet {int x1, y1, x2, y2, x0, y0, width, height;
Component c0 = null;Label l0 = new Label("Component:");TextField t0 = new TextField();
Button b0 = new Button("Create");Button b1 = new Button("Move");Label l1 = new Label();Choice c2 = new Choice();Label l2 = new Label("String:");TextField t2 = new TextField();
Button b2 = new Button("Call");
Method[] meths; int[] maps;
public void init() {setLayout(null);add(l0); l0.setBounds(10, 10, 60, 30);add(t0); t0.setBounds(80, 10, 200, 30);add(b0); b0.setBounds(290, 10, 60, 30);add(b1); b1.setBounds(360, 10, 40, 30);add(l1); l1.setBounds(10, 40, 380, 30);add(c2); c2.setBounds(10, 70, 200, 30);add(l2); l2.setBounds(10, 110, 60, 30);add(t2); t2.setBounds(80, 110, 200, 30);add(b2); b2.setBounds(290, 110, 40, 30);b0.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {try {c0 = (Component)Class.forName(t0.getText()).newInstance();
Sample27.this.add(c0);c0.setBounds(x0, y0, width, height);
t0.setText("");
meths = c0.getClass().getMethods();
maps = new int[meths.length];c2.removeAll();for(int i=0,k=0; i < meths.length; ++i) {Class[] arg =meths[i].getParameterTypes();if(arg.length == 1 &&arg[0] == String.class) {c2.add(meths[i].toString());
maps[k++] = i;}}
} catch(Exception ex) {l1.setText(ex.toString());}}});
b1.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {if(c0 != null) {}private void calcbox() {x0 = Math.min(x1, x2); y0 = Math.min(y1, y2);width = Math.abs(x1-x2);
height = Math.abs(y1-y2);}
public void paint(Graphics g) {g.drawRect(x0, y0, width, height);
}
}

10.6

本節のまとめ□ 自己反映機能→「プログラムの中で、プログラムそのものを操作できる」→より柔軟なソフトウェア構成が可能になる• その反面、プログラムが分かりにくくなる• どこまで「静的」(コンパイル時)、どこから「動的」(実行時) の線引き→トレードオフを考えることが重要□ ついでに:オブジェクト指向言語の諸側面のまとめ• 非常に多数の機能がある、ということは分かったと思う• それらすべてに「発明された理由」はもちろんある• しかし「それらがすべてが今いるのか?」は別の問題• 言語の多様な機能を使えば使うほど「難しく」もなる→「機能の増加」と「簡潔に/分かりやすく記述でき

20

る」のトレードオフについて常に考える (例:Java

vs C++)

11.4

アプリケーションフレームワーク□ 抽象メソッド:

親クラスで「自分自身へのメソッド呼び出し」を用いたメソッドを定義→子クラスでそれらのオブジェクト指向の利用技術

11

メソッドを具体的なものに差し替え□ オブジェクト指向言語→これまでの言語にないさまざま
な「道具だて」(例:□ これをさらに発展させて、汎用的なアプリケーション全体の構造を予め定義しておく→その中のいくつかのクラサブクラス、動的分配、継承、委スをサブクラス化してそこに各アプリケーション固有の譲、インタフェース、…)

部分を記述することでアプリケーションを完成させる• それをどのように使うか→なかなか難しい問題• 例:□ どのような方向での利用があるか?

• うまく当てはまれば生産性は高まるが、何をどうサ
ブクラス化するか、サブクラスはどのような規約に従う必要があるか、といったことを学ぶのが大変

オブジェクト指向にあった設計

11.1□ たとえば、アプレットもアプリケーションフレームワー□ 美しいプログラム構造 (何が美しい???) ←オブジェク

クの 1 つト指向言語の特性に合ったプログラム設計← OOSE(オブジェクト指向分析、オブジェクト指向設計、UML、統一
メソッド):

MFC、ET++、Choices、…

• アプレットはクラス Applet のサブクラスとして作本講の範囲外り、必要なメソッドをオーバライドする• init() → 初期設定する• paint() → 描画する

再利用

11.2

• start()、stop() → ページの表示開始/終了□ 再利用←書くよりは書かないで済ませた方が生産効率は• アプレットの場合は難しいカスタマイズはしないとよいに決まっているいう感じ• なぜオブジェクト指向で再利用?

→ クラス、オブジェクトといった単位は従来の「サブルーチン」よ11.5

り固まりが大きく、再利用に向いている• 再利用するものは何?

コード?

設計知識?

→ さ

コンポーネント□ ここまでの再利用技術→基本的にクラス (群) が対象→

まざまなレベルがある

プログラマ向け (クラスベースとも言う)□ プログラミングをしない人に使える再利用技術→インス
タンスベースの再利用

クラスライブラリ

11.3• インスタンスを生成し、そのプロパティ(属性、要す□ Smalltalk →充実したクラスライブラリが付属→クラるにインスタンス変数の値) をカスタマイズする

スライブラリを熟知すれば生産性が高まる、というブー• カスタマイズしたインスタンス群をディスク等に保存しておき、それを取り出してそのまま動かす

ムに• 実際にやってみると、よいクラスライブラリの開発/
クラスライブラリに熟知した人材の育成ともに簡単• そのようなインスタンスを「コンポーネント」と呼んでいる。ソフトウェア開発のためのコンポーネン

ではない

ト群→「コンポーネントウェア」、コンポーネント□ Smalltalk クラスライブラリでは差分プログラミング

ウェアに基づくソフトウェア開発→「部品組み立て(子クラスで親クラスの機能を少しずつ拡張していく)を多用→これもよい手法だと一時思われていた

プログラミング」□ 代表的な成功例→ VisualBasic (部品:• しかしやってみると、よい差分プログラミングは難しい (クラス間の依存関係が大きくなりぐちゃぐちゃになりやすい)VBX、OCX…

COM、DCOM の部品)
• ほかにも国産の IntelligentPad、Java ベースのJavaBeans などいろいろある□ 現在では、クラスライブラリはもちろん必要だが、整った機能を一式、分かりやすいインタフェースで提供するという当たり前の結論に

21• しかし、部品とその配線だけでできるプログラムで十分なの?

デザインパターン

11.6□ パターン:• そのサブクラスとして MacWindow、X11Window など
それぞれ用意する「繰り返し現われるようなカタチ」□ (ソフトウェアにおける) デザインパターン:• WinFactory という抽象クラスを用意し、メソッドとオブジェ

して makeWindow、makeButton 等を用意する

クト指向ソフトウェア開発において、有効に使えるよう• MacWinFactory、X11WinFactory などの具象クラス
でこれらをそれぞれ実装するなオブジェクト群の構成のパターン• アプリケーションの実行開始時に MacWinFactory な• 1990 ころから、Peter Cord 他がはじめた。日本では「ガンマ本」が有名になっている。ガンマ本はよどのインスタンスを作って WinFactory 型の変数に格納し、以後それを利用する

く使うパターンを集めた「パターンカタログ」になっている。

• なぜデザインパターン? → オブジェクトの接続関係のノウハウはかなり難しい (ちょっと思いつかない11.7□ オブジェクト指向にはさまざまな「道具」が含まれていようなものもある) →それを蓄積しておいて流通さる→その「道具」をどう使うか、についていろいろな工

せると、うまくはまったときに役立つ
□ 例:

夫がある

Command パターン

□ しかしこれらもまだほんの一部?

• メニューの選択、画面上のボタンなどはどれも「何らかの動作」を起動する→「動作をするオブジェク
ト」を用意して、それをメニューやボタンに結びつ→これからより多くの「よりよい利用方法」が現われる (はず)□ それらの利用方法を個別に「新しい」と思って受け入れるだけでは、流行に追われるだけ→必要なこと:

けていけばよい。
□ 例:

本節のまとめ• その「新しい」技術がこれまで行われてきたさまざ
まなことの中にどのように位置づけられるのかを考

Adapter パターンえる• ボタンが押されたときに呼び出されるメソッドはある名前に決まっている。しかし実際に起きて欲しい• 結局、ソフトウェアの生産において「自分が必要とすることは何か」をまず考え、それに照らして必要ことを実行するメソッドは別のメソッドである→「仲介するオブジェクト」を用意して、それが橋渡しをなものを取捨選択するすればよい。
□ 例:

Visitor パターン12

第 2 回課題• オブジェクトの階層構造で構造化グラフィクスとか□ 以下の課題から 1 つ以上 (4 を含む場合は 2 つ以上) を複合文書のようなものを作ったとする。「印刷する」
選択してプログラムを作成し、報告せよ。考察まできち「表示する」「ファイルに保存する」「スペルチェック
んと書くこと。
する」等それぞれの場合について、各クラスにそれ用□ (1) 「円」「正方形」などの図形が表示される例題のどのメソッドを作るのは面倒である → どうする???
れかを改良して、新しい図形を追加してみよ。また、「鉄□ Visitor パターンとは:アレイ型」を追加するものとして、その実装をどのように工夫するか検討し (例:• 各オブジェクト側には「accept」というメソッドを
1 つだけ用意しておく。その引数として、「印刷用の Visitor」「表示用の Vsitor」などさまざまなを組み合わせる)、実際に実現してみよ。
□ (2) 「円」
「正方形」などの図形が表示される例題では、図形の動きは外側から操作していた。しかしそれぞれのViritor オブジェクトをそのつど渡せばよい□ 例:

図形が自分固有の「速度」を持って動く方がオブジェク

AbstractFactory パターン

ト指向らしい。そのような形にクラス群の機能を拡張してみよ。できれば「速度」などの情報は実際に動く図形• Windows でも Mac でも X11/Unix でも同じに動作すにだけ付随していることが望ましい。 (複数の実装方法

る GUI アプリケーションを開発するには???
□ AbstractFactory パターンとは:

円と正方形のインスタンス

を試してみられるとなおよい。)□ (3) ボタンその他の GUI 部品を使って、「ボタンを押す
度にさまざまな色や大きさの円ができ、位置を調整でき• Window、Button、Dialog などの汎用的なクラスを用意するる」プログラムを作れ。または「電卓」を作れ。できれ22

ば「押した時の動作が場面によって変わってくるような
ボタン (ただし if 文による振り分けではなく実現)」をつけてみるとよい。
□ (4) その他、Java を用いて「オブジェクト指向言語らしい」プログラム (アプレットでもアプリケーションでもよい) を作成せよ。

23