電子工業振興会先端ソフトウェア技術委員会 1999.7.9— Java 言語に見るプログラミング言語の諸側面 —久野靖∗

1999.7.9

はじめに

1• パーサジェネレータ
• マクロプロセッサ← (佐々先生:助手)□ 二木先生からのリクエスト→「Java 言語のプログラミ• Algol68

ング言語的な側面」• Ada• Java 言語のプログラミング的でない側面って?

□ 「世の中には様々なプログラミング言語がある」• たぶん、政治力学的 (ビル・ゲイツ的?) 側面はいらな□ 大学院時代 (助手:米澤先生)いという意味?→○• 言語を離れた各種応用もいらないという意味?→○• 米澤先生が MIT から CLU の仕様を持ち帰っていた• 佐渡さん (現群馬大) が CLU のコンパイラを作った□ 飯泉さんからお伝え頂いた「皆様の要望」• みんな CLU で色々なシステムを書いていた• 言語の歴史 (e.g. CLU、Ada、…) から見た Java の言語としての美学→○• 修論:かな漢字変換システム (←「ソフトウェア作法」
本作成)• Java プログラマが Toy Program の世界から脱却す• 博論:CLU のような言語のための設計技法 (←「オブるためには→△

ジェクト指向設計」?)• 言語実装上の視点から見た Java の派生言語の目的/□ 「抽象データ型の概念は極めて強力かつ重要」

課題/今後→○□ 「ソフトウェア工学は自分には向いていない」□ 飯泉さんのお言葉□ 東工大助手→筑波大 (現在)

• 久野の得意とする話題について→◎

1.1□ オブジェクト指向言語の研究→ Misty• 強い型のオブジェクト指向言語

久野の経歴• 型パラメタを持つオブジェクト指向言語□ 東京工業大学理学部情報科学科木村研究室• 界面と実装の分離→界面は継承し、実装は委譲による□ 学部のころ…

□ 並列オブジェクト指向言語の研究→ p6/p6i• 木村研と井上研 (隣の研究室) はプログラミング言語研究のメッカ• 並行制御/同期機構→抽象状態同期• 抽象状態同期による継承異常の解決• 文字列処理言語• Pascal□ 「プログラミング言語研究には無限に課題が存在する」• Lisp□ 「新しいプログラミング言語を普及させるのはとても難• Ratfor (←ソフトウェア作法)しい」• 卒業研究→ Kernighan/Plauger 本を見ただけで• → Javaっていったい…

(!)C のコンパイラを作る□ 隣の井上研 (←大槻さん) では…
∗ 筑波大学大学院経営システム科学専攻

1

Java 言語の由来

1.2

□ 例:

□ Sun Microsystems 社→ Unix WS のイメージだがソフ

import java.io.*;

ト屋も一流

public class Sample2 {public static void main(String args[])
throws Exception {BufferedReader rd = new BufferedReader(
new InputStreamReader(System.in));while(true) {System.out.print("> "); System.out.flush();String s = rd.readLine();if(s == null) break;int i = (new Integer(s)).intValue();
System.out.println("" + i + " + 1 = " + (i+1));}}}□ 90 年代前半に、セットトップボックスやディジタル家電
用のシステムを開発するプロジェクトを開始• オブジェクト指向は必須→最初は C++を考えていた→ C++では安全なシステムにならない• C++をもとに危険な/複雑な機能を削除→ Java• 「アプレットのための言語」として爆発的に普及→
好運な側面• 言語そのものはまっとうな汎用のオブジェクト指向
言語→「まっとう」とは?

2.1

以下の進め方…

1.3

数を読み込み 1 たす

手続き型 .vs. 関数型とオブジェクト指向□ 関数型オブジェクト指向言語?

□ 系統的/網羅的に話をしようとすると難しい□ 関数型言語→副作用を持たない関数の組合せによるプロ• 時間も限られているし、「つまらない」「当り前の」話

グラミング

も含まれる

□ 「もの」の同一性を考えない。あくまで「値」• もっと「面白い」ことを重視□ 抽象データ型は副作用がなくても作れるが…

□ →行きあたりばったりに/思い付くままにテーマを挙げ□ その抽象データ型を「オブジェクト」として扱うのはや

て検討

りにくい

• さらに、あくまで久野の「独断と偏見」(きっと反論
多数?)• ある時点の人とその人が何かを行った後の人が「別人」になってしまう□ →面白いと思ったところで遠慮無く止めて議論してくだ• ないし「人」のインスタンスは存在せず、「人」の「痕跡」「スナップショット」の集まりを扱うことになっさい (お願い)てしまう

2

Java は手続き型言語□ やっぱり「オブジェクト指向言語」は「副作用」が必要
では?

□ 手続き型 (命令型) 言語とは…

• ただし、immutable なオブジェクトも当然あってよい• 文単位の順次実行、変数、代入□ Mutable なクラスの例• 本質的に「副作用」ベース (代入=副作用、だから)• 副作用はあっていいのか?

有害ではないのか?

import java.io.*;public class Sample3 {public static void main(String args[])
throws Exception {Human h = new Human(20, 55);h.addAge(5);h.addWeight(10);System.out.println("result: "+h);}static class Human {int age, weight;
public Human(int a, int w) {age = a; weight = w;}

□ その他「普通の手続き型言語」らしい側面について• スコープ• 制御構造□ 例:

Hello World

public class Sample1 {public static void main(String args[]) {System.out.println("Hello, World.");
}
}

2

2.3

public void addAge(int i) {age += i;}public void addWeight(int i) {weight += i;}public String toString() {return "Human("+age+","+weight+")";}□ Java の制御構造…C とおんなじ!

if、while、for、

do-while、switch• ただし goto はない (代わりに例外機構がある)• とっつき易さのために C とおんなじにしたのはいかがなものか?

}}□ 現在の言語のトレンド (?) は「begin-end」「{…}」の要らない言語」□ Immutable なクラスの例• Bourne Shell、CLU、Ada --- if … elseif …
else … end

import java.io.*;public class Sample4 {public static void main(String args[])
throws Exception {Human h1 = new Human(20, 55);
Human h2 = h1.addAge(5);Human h3 = h2.addWeight(10);System.out.println("result: "+h3);}
static class Human {int age, weight;
public Human(int a, int w) {age = a; weight = w;}
public Human addAge(int i) {return new Human(age+i, weight);}
public Human addWeight(int i) {return new Human(age, weight+i);}
public String toString() {return "Human("+age+","+weight+")";
}
}
}

2.2

制御構造

• Perl --- 文が 1 つでも「{…}」で囲むことを強制

2.4

例外機構

□ 「エラー処理のための goto」の代替?

• 「新しい言語には当然の機構」といいつつ…
• Lisp、CLU、Ada とかなり古くからあったが、普及したのは C++への例外処理の追加と Java の普及から?
□ 例外ならではのよさ• 処理の mainstream をごちゃごちゃさせない• 同種のエラーをまとめて 1 か所で処理できる□ Java での構文
try {←この辺の { } は省略できない!
文...
} catch(クラス名 変数) {...
} catch(クラス名 変数) {...
} finally {...
}ブロックスコープ□ 内側ブロックで同名の変数を宣言→外側の名前を隠す• Algol60 以来→「自由に埋め込める」ことから始ま□ Java ならではの特長:った?

• 例外をクラスで表し、その包含関係で「どの範囲の例外を受け止めるか」を指定する• 読みにくさやバグの原因になりやすい• C ではあまり問題にならないが、オブジェクト指向

Throwable
Error...(各種のエラー)
Exception
RuntimeException...(実行時に発生し得る各種の例外)
その他の例外...

だとインスタンス変数/クラス変数を隠すことになるので問題が増す□ CLU では…

• 内側スコープで外側と同名の変数を定義することを許さない• 例外もオブジェクトなので任意のデータ構造を持つことができる• インスタンス変数は「変数」ではなく「レコードの欄」(後述)

3• finally ブロック --- 例外で脱出するときも必ず□ 例:

Java とオブジェクト指向

3

実行される処理を記述できる□ 「オブジェクト指向」「オブジェクト指向言語」の定義…?

自前の例外を定義• △「レシーバ セレクタ 引数…」のようなメッセー

public class Sample5 {public static void main(String args[]) {try {int i = (new Integer(args[0])).intValue();
System.out.println("sqrt(i) = "+mysqrt(i));} catch(Exception e) {e.printStackTrace();}}private static double mysqrt(int i)
throws Negative {if(i < 0) throw new Negative(i);return Math.sqrt((double)i);}
static class Negative extends Exception {int value;
public Negative(int i) { value = i; }public String toString() {return "Negative: <"+value+">";}}}

ジ送信式 (cf.

CLOS)• △「クラス」を持つ (cf.
• △「継承」を持つ (cf.

Self)

多くの並列オブジェクト指向言語)□ 結局、どれか 1 つの性質を持って「オブジェクト指向言語」とは言えない• ○オブジェクト指向→「もの」と「もの同士の関係」に基づいてシステムを見るという「思想」「考えかた」• ○オブジェクト指向言語→「オブジェクト指向の考え方をサポートしてくれるような言語」

3.1

オブジェクト指向言語の「純粋さ」□ Smalltalk-80 →「純粋な」オブジェクト指向言語• すべての値がオブジェクトである

2.5• すべての操作がメソッド呼び出しである

例外の受け止めと伝播• 制御構造もオブジェクトのメソッドである (→拡張□ 言語によってさまざまな流儀□ 言 語 サ ポ ー ト → あ り/ な し ( 例:

可能言語???)

C の signal、• クラスもオブジェクトである (→自己反映計算)

setjmp/longjmp)□ 例外の種別→あらかじめ決まったもの/ユーザ定義可能

3.2□ 例外の宣言→しないと投げられない/宣言不要 (例:□ Simula、C++、Java →基本型 (整数、文字、実数、…)

Lisp)□ 例外の伝播→必ず受け止める必要 (例:「すべての値がオブジェクト」とは?

はオブジェクトでない→効率のため

CLU)/同じ例外□ しかしプログラマにとっては極めて不便が伝播□ 例外の受け止めのチェック→あり/なし• 「Object 型の変数に何でも入れられる」性質に制約が生じる□ Java では例外は原則として宣言しないといけない• 基本型を操作するメソッドの置き場所がない• ただし Error と RuntimeException はどこでも発生• Java ではこのために包囲クラス (wrapper class)を設けている

し得る→宣言を強制すると繁雑すぎるため「あらかじめ宣言されている」ものとみなす• しかし結局、int と Integer、float と Float などを使い分けるのは繁雑• 上記以外の例外を投げるメソッドを呼ぶ場合は→必ずその例外を捕まえるか、または自分自身も同一の例外を投げられる必要がある→ (先の「main(...) □ Smalltalk-80 ではすべての値はオブジェクト (Lisp とthrows Exception」はこのため)
かもそう)□ 言語としては、すべての値がオブジェクトである方が絶□ この辺、「きちんとさせる」思想なのは好感が持てる

対よい• 「エラーフラグ」「戻りコード」なんかだと無限にさ □ 例: Vecotor に色々なものを格納する
ぼれる…

• その意味ではもっと「きちんとさせる」手も4

□ 何がよいか?

import java.util.*;• 制御構造が拡張できる…本当に拡張するんだろうか?

public class Sample6 {public static void main(String args[]) {Vector vec = new Vector();
vec.add(args);
vec.add("Hello, World.");vec.add(new Integer(10));
vec.add(new Character(’X’));for(Enumeration e = vec.elements();e.hasMoreElements(); ) {Object o = e.nextElement();
System.out.println(o);
}
}
}

3.3

• 動作の断片を引数等で渡せる→柔軟性□ Java では「内部クラス」「無名内部クラス」でかなり対応□ 無名内部クラスを用いた whileTrue:
public class Sample7 {static int x = 0;
public static void main(String args[]) {(new Block() {public boolean doit() { return getX() < 10; }}).whileTrue(new Block() {public boolean doit() {System.out.println("> "+getX());
setX(getX()+1);
return true;}});}
public static void setX(int i) { x = i; }public static int getX() { return x; }static abstract class Block {public abstract boolean doit();public void whileTrue(Block b) {while(this.doit()) b.doit();}}}「すべての操作がメソッド呼び出し」とは?

□ Smalltalk-80 では「1 + 2」は「1」というオブジェク
トに「+ 2」というメッセージを送ることに相当。
□ CLU では「1 + 2」は「int$add(1,2)」の構文糖• C++では「operator+() によってユーザ定義クラスに演算子をつけられる• Java では「+」は基本型どうしの演算に限定□ メソッド呼び出しだと何がよいか?

• オーバライドの可能性→基本型そのもにに対しては

型/値/データ構造

4

なさそうだが…

• ユーザ定義クラスを使う際の「見ためのよさ」→結 □ 型とは→値の種類
構重要 (STL など)• 値の種類とは→「その値にどういう操作が施せるか□ Java でも「x + y」を「x.add(y)」に機械的に書き換えの集合」(抽象データ型的みかた)
るようにコンパイラで手直しすることはごく簡単→あっ□ オブジェクト指向言語では…「値 (オブジェクト) の種てもよい機能では???
類」==「クラス」→クラスを型と対応させるのは自然な

3.4

帰結

「制御構造もメソッド」とは?

□ Smalltalk-80 の場合→「ブロックオブジェクト」が重

4.1

要な役割

さまざまな型□ Pascal では…基本型 (整数、実数、文字、列挙型)+構• ブロックとはメソッドのコードの断片だがそれ自身オブジェクト

造型 (レコード、配列、集合、ポインタ)z ← [x ← x + 1. ↑ x] value.
z ← [:n | x ← x + n. ↑ x] value: 100.

• C でもだいたいこれに相当 (union が加わっている)• Boolean クラスや Block クラスのメソッドで制御構 □ C++では…クラスは「レコードの特別なもの」
造を実現• クラスは必ずしも参照 (ポインタ) でない→クラス値(x < 10) ifTrue: [x ← x - 1] ifFalse: [ ... ].
[x < 10] whileTrue: [ ... ].

の埋め込み• 記憶領域管理の面で多少いいこともあるかも知れな
いが…

• そのほかにも map 関数、整列の比較関数、探索のマッチ関数などもブロックとして渡すスタイル (Lisp で• プログラミングの上ではすごく繁雑になるだけ

はあたりまえ)5Java における型

4.2

□ 例:

□ 前述のように、基本型は特別扱い→不満な点

public class Sample9 {public static void main(String args[]) {Node n = new Node(’+’,
new Node(’*’, new Node(’1’),
new Node(’2’)),
new Node(’3’));print2(n); System.out.println();n.print1(); System.out.println();}public static void print2(Node n) {int i = 0;
Node[] a = new Node[100];a[i++] = n;while(i > 0) {Node n1 = a[--i];
System.out.print(n1.ch);if(n1.right != null) a[i++] = n1.right;
if(n1.left != null) a[i++] = n1.left;}}
static class Node {public char ch;
public Node left, right;
public Node(char c, Node l, Node r) {ch = c; left = l; right = r;}public Node(char c) {ch = c; left = null; right = null;}public void print1() {System.out.print(ch);if(left != null) left.print1();
if(right != null) right.print1();}}}□ それ以外の値→すべてオブジェクト型、すべてのオブ
ジェクト値はポインタ→簡潔で好ましい点• GC が前提となる→まっとうなオブジェクト指向言語
では当然のこと□ よくある話題:「列挙型が欲しい」• Java では int 型の final(書き換え不能) 変数に定数値を入れることで代用• これでは C の#define と同じという批判も• その代わり、クラスをまたがって違う値を設定できるという話も□ 例:

列挙型の代用public class Sample8 {public static void main(String args[]) {System.out.println(X.x1);
System.out.println(X.x2);
System.out.println(Y.y1);
System.out.println(Y.y2);}
public static class X {public static final int x1 = 0;
public static final int x2 = x1 + 1;}
public static class Y {public static final int y1 = X.x2 + 1;
public static final int y2 = y1 + 1;
}
}

4.3

スタックによる辿りと再帰的辿り

データ構造とオブジェクト指向

4.4□ データ構造→そのデータ構造を表すような値の構成→型

型検査□ 弱い型の言語→変数に型がない。データには通常、実行→クラス時に定まる型がある (例外:Bliss、BCPL、…)□ 静的なデータ構造→配列、レコード、union、その組合せ□ 強い型の言語→変数に型がある。コンパイル時に型検査を行う• 配列はある (後述) が、レコードはクラスに対応?
union は?

• 厳密な型の言語→変数の型と実行時の型は常に一致→実行時の検査は不要→実行時の効率はよい/柔軟性はない□ 動的なデータ構造→ポインタでつなぎあわせて構成

int x = 1; ←左辺と右辺は常に型が一致• オブジェクト指向にはポインタはない???
• C、C++では…

• 厳密でない強い型の言語→変数の型と実行時の型が同じとは限らない→実行時の検査が必要→実行時のstruct node {xxx info;
struct node *left, *right; } ...

情報も必要/柔軟性がある• Java では…

□ オブジェクト指向言語では→「親クラスの変数に子クラスが格納できる」(Simula 以来の規則)

class Node {xxx info;
Node left, right; ←「ポインタだから」と見る?
...
「再帰的な定義」と見る?
}

Object obj = new X();...
X x1 = (X)obj; ←実行時検査

6

• (ある程度まで)union 型の機能を提供している

print(new UnionPoint(new Point(10,100)));
print(new UnionInt(5));• ただし「あるクラス以下」という範囲なので「これ}public static void print(Union u) {u.print();}
static abstract class Union {public abstract void print();}
static class UnionPoint extends Union {Point pt;
public UnionPoint(Point p) { pt = p; }public void print() {System.out.println("Point: "+pt.getX()+","+pt.getY());}}
static class UnionInt extends Union {int it;public UnionInt(int i) { it = i; }public void print() {System.out.println("Int: "+it);}}とこれ」とは言えない。基本型も wrapper に入れな
いと使えない。
□ 例:

Object 型にいろいろなものを入れて取り出す

import java.awt.*;
public class Sample10 {public static void main(String args[]) {print(new Point(10,100));
print(new Integer(5));
print("ABC");}
public static void print(Object o) {if(o instanceof Point) {Point p = (Point)o;
System.out.println("Point: "+p.getX()+","+p.getY());} else if(o instanceof Integer) {int i = ((Integer)o).intValue();
System.out.println("Int: " + i);} else {System.out.println("???: " + o);
}
}
}

}

クラス/インタフェースと継承

5□ クラス方式のオブジェクト指向言語→オブジェクト指向

4.5

言語の主流

実行時の型と動的分配• cf. CLOS 方式、Self 方式、…

□ 動的分配 (dynamic dispatch) →オブジェクト指向の• 言語屋にとっては「遊べる材料」が増えたことに…

強力さの源の 1 つ• 基本的には「インスタンスのクラスに応じたメソッ
ドの選択」

5.1

Vechile v = ... (Car、Bike、Track)...
v.turnRight(); ← v に格納された実態によって
どのメソッドを呼ぶか自動選択

クラスの役割□ 「データの種類」(型) を定義し、名前をつけること→
抽象データ型に他ならない• これが CLU などだと次のようになってしまう• クラスを「雛型」としてオブジェクト (インスタン
ス、実体) を生成...
if v が自動車 then Car$turnRight(v)elseif v が自転車 then Bike$turnRight(v)...

□ クラス定義に含まれるもの…(Simula、Smalltalk-80
以来の伝統)□ なぜ強力なのか?

• クラス変数/インスタンス変数の定義• クラスメソッド/インスタンスメソッドの定義• △コードが短くて済む←それなりに重要• ◎コーディング時に想定していなかった選択肢が実 □ ということは、クラスは「実装を定め」ている。
行時に自由に追加可能 (乗物はいくつでも増やせ、そ• 実装を規定することを通じてインタフェースを定めの際に際コンパイルが不要)ている、ともいえる□ 例: 先の例を動的分配に書き換え
import java.awt.*;
public class Sample11 {public static void main(String args[]) {

7

5.2

継承

□ 言語として委譲を前面に出したもの→ Self
□ 例:

□ 継承の古典的解釈…(Simula、Smalltalk-80 以来の伝統) →インスタンス変数定義、メソッド定義を親クラス

public class Sample12 {public static void main(String args[]) {Thread tr = new Thread1();
tr.start();try {
tr.join();} catch(InterruptedException e) { }}static class Thread1 extends Thread {public void run() {for(int i = 1; i < 10; ++i) {try {Thread.sleep(500);} catch(InterruptedException e) { }System.out.println(i);}}}}から子クラスへ引き継ぐ• その結果として実体の互換性も生まれる• 変数やメソッド定義の追加→機能/ふるまいの追加(上位互換)• オーバライド (メソッド定義の差し替え) →互換性についてはプログラマ次第

5.3

継承の用途

□ 継承にはさまざまな用途が「相乗り」している• △親クラスを下敷にして少ない労力 (再利用) で新しいクラスを作る (差分プログラミング)• ○型どうしの親子関係を定義する□ 例:• ○動的分配の基礎となる□ 実装を利用したいだけなのに型としての関係ができるのは嬉しくない5.4

多重継承□ 複数の親から継承すること。継承の目的は上と同じ□ 言語仕様上も実装上も難しい• オブジェクトの形 (単一継承のように美しく実装できない)• 複数の親が共通の祖先を持っていたら?

簡単なスレッド/継承版(repeatedinheritance) → C++ではそれらを「重複して持つ」「重ねる」という 2 つの選択肢

簡単なスレッド/委譲版

public class Sample13 {public static void main(String args[]) {Thread tr = new Thread(new Runnable1());
tr.start();try {
tr.join();} catch(InterruptedException e) { }}static class Runnable1 implements Runnable {public void run() {for(int i = 1; i < 10; ++i) {try {Thread.sleep(500);} catch(InterruptedException e) { }System.out.println(i);}}}}□ 実装を多重継承するのは面倒で複雑

5.6□ Java も (クラスの/実装の) 多重継承は行わない

界面と実装の分離□ 結局「型どうしの関係」はインタフェース (界面) の問

5.5

題→界面についてのみ、継承ができればよい

継承と委譲• 界面は多重に継承しても問題がない (矛盾があれば検出でき、実装と違って動かしてみないとうまく行□ 委譲とは→「自分で実装しないメソッドを他のオブジェ
クトにたらいまわす」こと

くかどうか分からないということはない)□ メソッド定義を継承する代わりに、そのメソッドを「呼• 界面は継承、実装は委譲によればよい→ Misty の考
え方

び出しても」よい→委譲は継承の代わりに使うことができる• Java では実装は単一継承、インタフェースは多重継承• 継承で生じるいろいろな問題がない• 実行時に動的に委譲先を切替えることができる• デザインパターンで多用
8

5.7

Java のインタフェース機能

6.1

引数/返値機構□ 構文的にはクラスと同様だが、変数定義、メソッド定義 □ 教科書的な→「名前呼び」「値呼び」「参照呼び」を含まない (定数は定義できる)□ Java みたいにオブジェクトの参照を渡すのは?→「call• インタフェースもクラスと同様に型として扱われる

by sharing」(CLU での用語)、「オブジェクト呼び」• クラスはインタフェースを実装 (implements) できる• 実質的には値呼び• 副作用を返したいなら渡されたオブジェクトを変更□ クラスの継承「も」残っているのはどんなものか?

□ オブジェクトには変更不能なものもある• 継承が「一切ない」方がすっきりするけれど過激?
• 単一継承のクラス階層はそれなりにあった方がよい• →返値を複数持てるとよい (CLU、CommonLisp)。こという説もれが Java に入れられなかったのは不満 (JVM では実
装は何ら問題ないはず)。

□ より過激な考え方: クラスは変数の型としては使わな
い (使えないような言語にする) →各コードをよりよく実装から分離できる

6.2

IIII var = new CCCC(...);↑界面↑実装

オブジェクトアクセス□ インスタンスメソッドは暗黙のうちにカレントオブジェ
クト (this) を渡されている→標準的なやりかた• さらにこれを進めると「特定のクラスを決め打ちで書くことは避けたい」ことになる→デザインパター □ メソッド内側ではそのクラスのクラス変数、インスタン
ンの FactoryMethod パターンス変数へのアクセスが許される→「中が開いている」モ
デル。C++も同様。

5.8□ c.f. CLU では→「抽象データ型」と「内部実現型」のインタフェースの別の用途間の視点の切替えがある□ 処理系に「このクラスはこういうクラス」と教えるた□ このため、CLU ではオブジェクトの実現が整数型などでめに使う→メソッドが 1 個も定義されていないインタ

あってもよい。

フェースを使う• 例:• Java などでは必ず implicit record になってしまう (そうでない実装があってもよいかも???)implements Clonable → clone() してよい

ことを表す

• 例:

implements Comparable → 線形順序を持つ

ことを表す

6.3

コンストラクタ• 例: implements Serializable → 直列化可能で□ C++で始まった、オブジェクトを生成するとき自動的にあることを表す
呼び出される「初期設定メソッド」

6• コンストラクタの実行中はオブジェクトが「半完成状態」にあるので注意が必要

Java とメソッド• コンストラクタは「クラス名と同名」のため継承さ□ メソッドの実行モデル→ごく標準的な手続き実行

れない→何か間違っている感じがする (親のコンス• Pascal のように囲むブロックの変数をアクセスしなトラクタを呼ぶ機能はある)

いでいい→実現が容易• いっぽう、クラス変数/インスタンス変数はメソッド

6.4の外側にある変数として利用できる→それなりに便利

オーバローディング□ メソッド呼び出しでなく「メッセージ送信」という言 □ オーバローディングとは→ソースコード上では同名のメソッドをコンパイル時に識別して呼び分けることい方も (Smalltalk-80) →並列ぽい感じ。実際にはSmalltalk-80 でも手続き呼び出し。

• 演算子はもともと「オーバロード」されている (+→
整数の+、実数の+)• 並列オブジェクト指向言語だとこの辺は違う (Javaは並列オブジェクト指向言語ではない)9• さらに、メソッドオブジェクトを経由してそのメソッ• 言語として大々的にはじめたのは Ada、引数と返値

ドを呼び出すことも可能

による識別• 値の受け渡しには Object[] を多用。さらに基本型• Java では引数の個数と型だけが識別に使用されるなら包囲クラスを使用□ オーバローディング (静的に解決) と動的分配 (実行時に解決) がまざっているとやっぱりごちゃごちゃしない □ 例:
か?

統一した方が美しいのでは?

import java.io.*;import java.lang.reflect.*;• 些細な問題だが、コンストラクタは名前が決まっているので複数定義するとオーバロードにならざるを

public class Sample14 {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(); }}}public static class Test {int i;
public Test() { i = 0; }public Test(int n) { i = n; }public Test next() { return new Test(i+1); }public void print() {System.out.println("Test value: "+i);
}
}
}

得ない

Java と自己反映機能

7□ 自己反映機能とは→処理系自身に関する情報をプログラ
ム側で取得/変更するような機能• CLOS などでは「変更する」方もあり→たとえば動的分配のふるまいなどを修正することもできる• Java では「取得」だけサポート→それでも昔の C++
よりずっと使いやすい (現在の C++ではある程度サポートがある)7.1

型に関する情報□ オブジェクトの実行時の情報で一番必要なのは「このオブジェクトは具体的には何の型か?」• Java では instanceof 演算子で「ある型のサブクラスかどうか」を直接判定可能• しかしそもそも、「クラスオブジェクト」が存在し、すべてのオブジェクトは Object 型から継承している getClass() でそのオブジェクトが取れる→そのメソッドとかにしておいてもよかった???

• オブジェクトをダウンキャストする際、指定したクラス以下でなければ例外が投げられる→ instanceofと同じことだからこれでよい?

7.2

任意のクラスを生成してメソッドを呼ぶ

クラスオブジェクト□ クラスオブジェクト→ 1 つの型に対応 (インタフェースにも)□ 基本型はオブジェクトではないが、なぜかクラスオブジェ
クトはある→統一的に扱うため。かなり気持ちわるい

7.3□ クラスオブジェクトの各メソッドを使って、そのクラスのインスタンスがどんな変数やメソッドを持っているか、

JavaBeans と自己反映機能□ JavaBeans → COM/DCOM のようにインスタンスベースでまたそのシグニチャは何かなどがすべて調べられるの組み立てを可能とするアーキテクチャ• インスタンスベースのカスタマイズが可能である必要
10

内部クラスから囲む環境へのアクセス• ツールが「このインスタンスはどんな操作があるか」 8.3
を見て、その情報に基づいてカスタマイズを実行□ 内部クラスのインスタンスからそれを生成したインスタ• 具体的には「getXXX()」というメソッドがあればンスメソッドの this を参照できる (何段階でも) →当「XXX」というプロパティがあるものと判断する。さ然、自分の this もあるから、this が複数ある。それら

らに「setXXX()」というメソッドがあればそのプロを参照するには「クラス名.this」(気持わるい!)

パティは書き換え可能だと判断する• もっと高度なカスタマイズ→ ZZZ という Bean クラスと一緒に ZZZBeanInfo というクラスがパッケージ• 当然、それらのメソッドも (private でなければ) 呼されていて、なおかつそれが implements BeanInfo• このほか、ローカル変数や引数も final 指定 (つまり読み出しのみの変数や引数) なら参照できる

べるであれば、このクラスを用いてさまざまな情報を受け渡す

GUI と内部クラス

8.4

Java の内部クラス機能

8□ JDK 1.0.x までは、GUI 部品は「サブクラスを作ってそこで動作をオーバライドする」方式だった (Thread の□ JDK 1.1 から導入された機能

サブクラス版と同様)□ 単に「クラスの中にクラスが書ける」というだけ…

□ JDK 1.1 からは「部品はサブクラス化せず、アダプタク• だけど、結構奥が深く、使い手があるラスを設定する」方式に• 前提として、JVM を変更せず、コンパイラだけで実装←既に動いている多量の JVM に対する配慮← HotJava• 部品の抽象データ型を壊さずに動作を独立してつけられる

α 3 から JDK 1.0 への移行時の混乱

8.1• GUI 生成ツールなどにより適した方式 (特に Beansなどに)

内部クラスとは• 多数のアダプタクラス→内部クラスの使用が (ほぼ)前提に□ クラスの中にクラス定義が書ける• たとえば Adaptor パターンや Command パターンを使□ 例:うと「クラスだらけ」になるが、内部クラスにしておけばソースがごちゃごちゃにならずに済む

8.2

無名内部クラス□ 多くの場合、内部クラスは 1 箇所でインスタンスを生成してそれでおしまい→いちいち名前をつけたり考えるのは無駄な感じ• そこで、「このクラスのサブクラス」「このインタフ
ェースを実装してるクラス」という形で、名前は無名のままで済ますことができるようにした。

• 名前がないのでコンストラクタが作れない→代わりに「インスタンスイニシャライザ」を用意した←やっぱりコンストラクタは…

11

内部クラスによる GUI 部品のアダプタ

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class Sample15 extends Applet {Font fn = new Font("Helvetica", Font.BOLD, 24);
String str = "";Label lab = new Label(str);
Button b1 = new Button("A");
Button b2 = new Button("B");public void init() {setLayout(null);add(lab); lab.setFont(fn);lab.setLocation(20, 20); lab.setSize(200, 40);lab.setBackground(Color.white);add(b1); b1.setFont(fn);b1.setLocation(20, 80); b1.setSize(60, 40);
b1.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {addStr("A"); repaint();}});
add(b2); b2.setFont(fn);b2.setLocation(120, 80); b2.setSize(60, 40);
b2.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {addStr("B"); repaint();
}

9.1

});
}
public void paint(Graphics g) {lab.setText(str);}
public void addStr(String s) {str = str + s;}

パッケージ機能□ クラスを「グループ化し」「一意的な名前をつける」• ファイル先頭の「package a.b.c.d;」といった宣言
で指定• パッケージの階層構造とディレクトリの階層構造が対応する}• JDK 1.0.x では大袈裟だと思ったが JDK 1.2 ではこ

8.5

内部クラスの実装れくらいないと標準クラスでも大変。まして他のベンダーのクラスまで含めると…

□ JVM を変更しないという前提→ソースレベル変換で可能□ 標準以外のパッケージ名→ドメイン名をもとにつけるな設計• ただし名前は衝突しないようにシステムに予約された• 例:「$」を使う。名前があれば「OuterClass$Inner」、• ネットワーク上で一意的になる→なかなか興味深い

なければ「OuterClass$0」のような名前になる• java.*、javax.*については標準/標準拡張クラス用に SUN が予約

• 外側の this、ローカル変数、引数も「$」つきのイ
ンスタンス変数に格納 (final だと変更はされない

xxx.co.jp → package jp.co.xxx;□ クラス名の代わりに常にフルクラス名 (パッケージまで

からコピーを持てばよい)

含めた名前) を指定することができる□ 内部クラスのインスタンスから直接変数を書き換えない、というのは言語設計としてもそれなりによいと思う
し実装も楽ができる

9.2

名前空間

□ パッケージを指定しない場合は「無名パッケージ」に属8.6するものとして扱われる

内部クラスの使いで□ パッケージ名の規定は強制するメカニズムが (今のとこ□ 上述の Adapter などにはもちろん重宝ろ) ない□ 普通のオブジェクト指向言語なのに、Smalltalk-80 の□ ということは「名前の衝突」は考える必要がある←単一

ブロックがあるみたいな→なんか変な気分

言語系における昔からの問題• もちろん、アイデア次第でいろいろに使える□ Java での解決策→「クラス名単独ではなく、(クラス名、• C の「関数ポインタを渡す」代わりに使うこともあり

クラスローダのインスタンス)」の対でクラスの一意性• × Microsoft は独自拡張で Java に関数ポインタを入れようとしたが…

を識別する」• システムクラスローダ→システムクラス (CLASSPATH
環境変数で指定されているディレクトリにあるクラ

9ス) をロード→システムクラスはすべてのプログラ

パッケージと名前空間

ムで共通□ ブラウザ上の複数アプレット、JavaOS などで分かるよ• AppletClassLoader →アプレットをロード/強制再ロードするごとにインスタンスができる→それぞれ

うに、JVM 上には多数の独立した Java プログラムが共存→名前の衝突とかは?

はたとえ同じコードであっても別の名前空間になる• その他特別なクラスローダを作った場合も同様• パッケージ機能→多数のライブラリを階層構造で整理• しかし名前空間の分離はパッケージと別に必要

10Java と並列□ Java は並列言語か?

(YES/NO)• Java の並列実行モデル→スレッド12• Java の並列実行に対するサポート→オブジェクトに}}
static class Getter extends Thread {BoundedBuf buf;
int count;public Getter(BoundedBuf b, int c) {buf = b; count = c;}public void run() {try {for(int i = 0; i < count; ++i) {System.out.println("got "+buf.get());}} catch(Exception e) { e.printStackTrace(); }}}static class BoundedBuf {int[] a = new int[10];int ipt = 0, opt = 0;
public synchronized void put(int v)
throws InterruptedException {while((ipt+1)%10 == opt) wait();a[ipt] = v; ipt = (ipt+1)%10; notifyAll();}public synchronized int get()throws InterruptedException {while(opt == ipt) wait();int x = a[opt]; opt = (opt+1)%10;
notifyAll(); return x;}}

ロックが組み込み• なぜ並列言語とは言えないか?

何が問題か?

Java の並列実行モデル

10.1□ スレッドモデル→ Thread ないしそのサブクラスのインスタンスに対して start() というと、指定したコードが独立したスレッドとして実行• ということは、オブジェクトとスレッドが直交している• c.f.

並列オブジェクト指向言語→オブジェクトがスレッドと密接に関連している□ 何がよくないか→共有メモリモデルの相互排斥をしながらのプログラミングを強いられる。Ada などの方がずっとまし• Par Brinch Hansen の批判

Java の並列実行に対するサポート

10.2□ オブジェクトにロックが付随している• synchronized(...) { ...
できる。ただし advisory} で排他領域を作成• 条件同期→ wait()、notify()、notifyAll() によ
る
□ 例:

}

10.3

何が問題か?

□ 並列実行単位間の競合が野放しで「手当したい人は

synchronized を使ってね」になっている→過去の経験の放棄

有限バッファ

public class Sample16 {□ 条件同期のためのキューがモニタ 1 つにつき 1 つだけ→
public static void main(String args[]) {BoundedBuf b = new BoundedBuf();
複数の条件で寝る場合に困る
Thread t1 = new Putter(b, 100, 20);
Thread t2 = new Putter(b, 200, 30);• 定石として「全員起こして、条件が OK の人以外はまThread t3 = new Getter(b, 50);
た寝る」ことを繰り返す→ものすごく無駄
t1.start(); t2.start(); t3.start();try {t1.join(); t2.join(); t3.join();} catch(Exception e) { e.printStackTrace(); }11 Java と分散}
static class Putter extends Thread {□ Java は分散言語か? (YES/NO)
BoundedBuf buf;
int start, count;□ 「 分 散 オ ブ ジェク ト 技 術 (distributed object
public Putter(BoundedBuf b, int s, int c) {technology)」→分散言語でないオブジェクト指向言
buf = b; start = s; count = c;
語で分散プログラミングをするための技術}
public void run() {□ 分散言語の方がいいのかどうか? いいとすればどうしtry {for(int i = 0; i < count; ++i) {て?
System.out.println("put "+(start+i));buf.put(start+i);}} catch(Exception e) { e.printStackTrace(); }

13

11.1

□ 要するに「ソケットプログラミング」→ネットワーク上□ Java の普及の原動力→アプレット→「ネットワーク経の Stream を使えばわりあい簡単

由でやってくるコード」→安全でなければ誰も使わない→セキュリティのための設計が最初からある□ しかし Java だからどうということはない

11.2

Java とセキュリティ

Java による古典的な分散プログラミング 12• 言語処理系/実行系に基づくセキュリティ→古くからあるアイデアだがこれほど本格的に普及したのは分散オブジェクト技術

Java が最初□ CORBA → JavaIDL + CORBA 製品□ いくつかの変遷• ○他言語との相互運用性がある• ○インタフェースから CORBA インタフェースができ• 〜JDK 1.0 →セキュリテイサンドボックス

る→有利な点• JDK 1.1 →署名アプレットによるサンドボックスからの逸脱• △汎用なだけあって遅そう• JDK 1.2 →より一般的なセキュリテイモデル□ HORB →国産!!!

• 電総研の平野聡さんが作った Java 用 ORB+コン
パイラ+ランタイム• ○ CORBA や RMI がまだなかったころから動いていた

12.1

セキュリティサンドボックス (〜JDK 1.0)

□ アプレットは「セキュリティサンドボックス」で実行→先見の明• その中ではファイル読み書き禁止、ネットワーク接• ？効率は「よい」という主張と「よくない」という続はダウンロード元サーバに限定主張がある• 実 装 → セ キュリ ティマ ネ ー ジャク ラ ス のcheckAccess() 等を呼んでチェック• △必ずしも標準でない、制約が強いなどの弱点□ RMI (Remote Method Invocation)• この中では、「何レベル以内にユーザクラスのコード• 普通の RPC。クライアントスタブ+サーバプロキシがあるか」を場面ごとにチェック→正しく実装するの• Java ならではの利点→スタブを予めインストールしなくてもネットワーク経由でロードして来られるが難しい□ RPC ベースでいいのか?

それってオブジェクト指向な

12.2

の???

11.3

署名アプレット□ サンドボックスの制約を逃れたいという強い要求• アプレットクラス群を JAR ファイルと呼ばれるアー

JavaSpaces/Jini

カーブ形式に格納し、全体に署名を施す□ Linda →古くからあるもう 1 つの (というのは RPC、メッ• 実行時に署名を検証できたら、サンドボックスを外す

セージ送信と対比して言っている) 分散言語モデル□ All or nothing というのは問題がある• TupleSpace と呼ばれる「エーテル」に「タプル」を送出→マッチングにより取り出す• 相手を特定しない/時間を限定しない通信が可能

12.3□ Java でこの通信モデルを採用することの意味…

JDK 1.2 のセキュリティモデル□ より柔軟なセキュリティモデル• 家電製品、ホームバスなどでは「どういうモノがつながっているかの発見」「それらの間でのプロトコルの交渉」などが必要• すべてのコードおよび実行のインスタンスには暗黙のうちに「権限」が存在している• あるコードから別のコードを呼ぶ際、権限は両者の• そのためには、「マッチングによる情報の取得」「時間に束縛されない」「誰がいるか分からなくても情報を送出できる」といった性質が極めて有用□ 言語そのものの話じゃなかったですね14

うち弱い方に合わせられる• それだけだと困るので、「ここは特にチェックしたから本来の権限に強める」という「おまじない」を用
意する

13.2

Object val = AccessController.doPrivileged(
new PrivilegedAction() {public Object run() {... // この中が権限が強い}});□ Java では B が A のサブクラスなら、B[] は A[] のサブ
クラス• なお、昔は次のようになっていたがこれでは問題 (なぜか?)• 実はこれは問題!!!
□ サブクラス互換性→ B が A のサブクラス⇒ A が持つメ

AccessController.beginPrivileged();... // この中が権限が強い
AccessController.endPrivileged();

ソッドはすべて B でも利用可能 (型検査を通る)• ということは、B が A と同名のメソッドを持っている場合、その引数はより広くなければいけない※
A#add(A x) をB#add(B x) でオーバライドしてはいけない!

モジュールパラメタ/型パラメタ

13□ モジュールパラメタ→「コンパイル時に束縛する」パラ• ただし Java では上の例はオーバライドにならない(引数が違うので区別可能)

メタ

• 例:

• また、B が A と同名のメソッドを持っている場合、その返値はより*狭く*なければいけない

配列の大きさみたいなもの□ モジュールが提供している機能が「型をつくり出す」場□ それが配列型とどういう関係にあるかというと???

合→パラメタによって違う型ができることも□ さらに、パラメタ自体も型であってよい→型パラメタ→• Java では B が A のサブクラスなら、B[] は A[] のサ
ブクラス• A[] に入れることができていたものを、 B[] には入れられない→これが※に抵触する

これだけあれば他のパラメタはあまり必要とされない• もともとは CLU、Ada など抽象データ型言語で始まる• しかし実は配列型なども型パラメタを持つと考えら

A[] a = new B[100];...
a[10] = new A(...);

れる

13.1

Contravariance/Covariance 問題←サブクラスだから OK←実行時に例外!

• あるべき解決策…(1) 配列の親子関係を設けない (2)
読み出しのみの配列に限って親子関係を設ける

Java と配列型□ 配列→「何を格納する配列か?」→パラメタのある型 (ないし型生成子)13.3□ 言語によりさまざまな流儀

型パラメタの難しさ□ 配列みたいに「入れておくだけ」のもの (コンテナクラ• C、C++では配列型はあるが変数はみなポインタ→さまざまな問題ス) は型パラメタを持たせてもすべてポインタとして一様に扱える→さほど問題がない• Pascal では配列は「要素型、添字型」をパラメタに □ 「常に昇順に並んでいる配列」→型パラメタを持つモ持つジュール (クラス) の中から、パラメタ型の操作を呼び• Java では配列は要素型のみをパラメタに持つ出す必要がある→難しい (特に型検査をきちんとしなけ□ 型パラメタ機構があれば、それを用いて統一的に扱える
はず?

ればならないので)• CLU、Ada など→「この型パラメタはどれどれの操作を持っている」という情報を明示的に指定する→大• Java では型パラメタ機構はなく、配列型だけが特別変繁雑扱いで、唯一のパラメタを持つ型 (ないし型生成子)• オブジェクト指向では→「このクラスまたはそのサブクラス」という情報があればどのような操作があになっている

るか分かる→これを bounded polymorphism と呼ぶ(上界となる型を bound と呼ぶ)。さらに、上界が型パラメタを持つ→ F-bounded polymorphism

15

13.4

オブジェクト指向には型パラメタ不要?

□ 上に述べたように、型パラメタの部分を消去して上界のクラスとしてコンパイルする□ 型パラメタが要らないわけ…あるクラスが「X」というク• 基本型は包囲型につつんで渡す (いまいち!!)

ラスを利用していたとすれば、いつでもその値を X のサブクラスで置き換えることができる→すべてのオブジェ □ 制約: パラメタ型のオブジェクトの生成ができない←
クト利用は暗黙のうちに型パラメタである
コンストラクタはそれぞれ違うはずなのにコードは 1 つだから□ 型パラメタが必要なわけ…???

• ○型パラメタであればコンパイル時にチェックできる□ キャストや instanceof が駄目←クラスが 1 つになってしまうから当然• ○型パラメタであれば実行時にチェックできる• 一般に均質な実装ではリフレクション関係は全滅に• ○型パラメタであれば効率よく翻訳可能なる• ◎型パラメタによって明示的に「切り替わる」箇所を表現できるべき□ GJ のもう 1 つの特徴:

既にコンパイル済みの (型パラ

メタを使っていない) コードをシグニチャだけから型パ13.5

ラメタを使ったコードに持ち上げる

Java 言語への型パラメタの導入□ 現在非常にホットな話題

NextGen における均質/不均質の混合実
装13.8□ 主要な論点→言語仕様、実装方法、JVM の変更の有無、
既存のコードの移行性□ 代表的な実装の流れ:□ これも前提として JVM は変更しない

Pizza → GJ → NextGen• 共通部分を GJ と同様にパラメタ消去して汎用クラス
として生成• http://wwwipd.ira.uka.de/~pizza/• その他にも研究としては沢山あるが、上の流れが「Sun• 各パラメタごとにそれに対応するインタフェースとクと仲良く」してるので将来の Java 言語に入るのかも

ラスとを対で生成 (パラメタ情報を name mangling
により付随させる) →クラスの方は汎用クラスのサ

13.6

ブクラスかつインタフェースを実装

実装方法の選択□ 均 質 な(homogenous)(heterogenous) 実装• パラメタ型のインスタンスを生成するなどの部分は、汎用クラス内では snippet と呼ばれる抽象メソッド

実 装 .vs. 不 均 質 なの呼び出しにしておく□ 均質とは→コード 1 つで様々な型パラメタに対応• パラメタごとのクラスでこの snippet をオーバライ
ドしてそこに適切なコードを入れてておく• コードが少なくて済む• さまざまな制約がある (例:ない)コンストラクタが使え □ JVM を変更すれば何とでもなるだけに、変更しないという前提に基づくチャレンジとはいえ、相当苦しい感じがする□ 不均質とは→型パラメタごとに別々のコードになる• C++のテンプレートなど→マクロ展開が前提のような言語仕様→不均質な実装になる→コードが増える

14

さいごに

• 混合した形として→共通部分を親クラスとしてくく □ こうしてまとめて見ると、Java は新しい言語だけにこれまでの (伝統的な手続き型系列の) プログラミング言りだした上で、それを継承する形でパラメタごとの語研究の成果がぎっしり詰まっていて、題材として興味
クラスを作るものもが尽きない。

13.7□ というわけで、つい欲張ってしまって準備が修羅場にな

GJ における均質な実装の手法

り、大変でした。

□ 前提として、JVM は変更しない。コンパイラのみで対処□ しかも reference をきちんと書く暇がありませんでし• バイトコードには JVM が直接参照しない部分があり、そこにパラメタ情報を記録する16た。どうも申し訳ありません。だいたいは WWW で検索すれば見つかると思います。