第5章 オブジェクト指向
これまで定義したクラスは、すべて JPanel クラスを継承したものだった。この章ではオブジェクト指向の概念をより良く理解するために、簡単なクラスを一から設計することにする。この章の例は規模が小さ過ぎて、再利用などオブ
ジェクト指向のありがたみがわかりにくいかもしれない。オブジェクト指向は規模の大きなソフトウェアでこそ活きる技術であり、本来はもっと大きな例を取り上げるべきだが、この章の例は おもちゃの例 (toy example) に過ぎないことを心に留めておいて欲しい。

5.1 クラス
まず、もっとも簡単な 2 次元座標を表すためのクラスから始める。クラスの定義は、今までも行なってきたが、今回は一から（継承を使わず）定義する ⦅実
は、java.awt.Point という、ほとんど同じようなクラスが Java の標準ライブラリーにあるが、それは今回は使わない。⦆ ので extends 以下がない。

詳細: 正確にいうとすべてのクラス型の暗黙のスーパークラスとなる
Object （より正確には java.lang.Object ）というクラスがあり、extends 以下がない場合は、… extends Object と書くのと同じことになる。
ファイル Point.java（バージョン 1）
1 public class Point {2// フィールド （インスタンス変数）3
public int x;
4
public int y;
5 }クラス は基本的には、いくつかのデータ（変数）をひとつのまとまりとして扱えるように部品化したものである。配列は同種のデータをまとめたものである
が、クラスは異種のデータをまとめることができる。
上の例では Point という名前のクラスを定義している。x と y は、このクラスのである。（メンバー変数, インスタンス変数 という呼び方も用いる。）この例では、たまたまフィールドの型がすべて同じ int であるが、もちろんフィールドの型はバラバラで構わない。

5.2 クラスの使用
Point などのクラスの名前は、intなどの Java にもともとある型名と同じように使うことができる。例えば p という変数が Point クラスに属することを宣言するためには、
Point p;

オブジェクト指向言語 第 5 章 p.1

のようにすれば良い。このような変数を初期化するためにはというキーワードと、クラス名を用いて、
p = new Point();と書く。このとき、新しい Point クラスの（instance , 具体例という意味）が生成されて、p という変数に代入される。Point クラスのインス
タンスは、今の定義の場合、int を 2 つ持ち、自分が Point クラスに属するという情報も持つデータである。
実際の使用例は次のような形になる。
ファイル PointTest.java（バージョン 0）
1 public class PointTest {2// version 0
3public static void main(String args[]) {4
Point p = new Point();5p.x = 1; p.y = 2;
6
System.out.printf("(%d, %d)", p.x, p.y);7}
8 }オブジェクトのフィールドには「」（ドット）演算子 を用いてアクセスする。. の前にオブジェクト、後にフィールド名を書く。

詳細: PointTest.java と Point.java を同じディレクトリーに置いておくと、PointTest.java をコンパイルすれば、 javac が自動的に依存関係を見つけ出して、Point.java もコンパイルする。

なお、次の例を実行してみるとわかるように、別々のインスタンスのフィール
ドは別々の領域に割り当てられている。
ファイル PointTest.java（バージョン 0b）
1 public class PointTest {2// version 0b
3
public static void main(String args[]) {4
Point p1 = new Point(), p2 = new Point();5
p1.x = 1; p1.y = 2;6
p2.x = 9; p2.y = 8;
7
System.out.println("(" + p1.x + ", " + p1.y + ")");
8
System.out.println("(" + p2.x + ", " + p2.y + ")");
9
}
10 }

問 5.2.1 上の PointTest クラス（バージョン 0b）+ Point クラス（バージョン 1）を実行したときの出力を書け。

5.3 メソッド

オブジェクト指向言語 第 5 章 p.2

これまでのクラスの使用法は C の構造体にほぼ相当する。このままではオブジ
ェクト指向の一歩手前である。実際にはクラスはもっとパワフルな概念であり、オブジェクト指向を使いこなすには、その差の部分を知る必要がある。
まず大事なことは、クラスの中には、関数（, メンバー関数 ）を定義することができるということである。
ファイル Point.java（バージョン 2）
1 public class Point {2// フィールド(メンバー変数）3
public int x;
4
public int y;
56// メソッド（メンバー関数）
7public void move(int dx, int dy) {8x += dx;9y += dy;10}
1112
public double distance() {13
return Math.sqrt(x * x + y * y);
14
}
15
16
public void print() {17
System.out.printf("(%d, %d)", x, y);
18
}
19
20
public void moveAndPrint(int dx, int dy) {21
print(); move(dx, dy); print();
22
}
23
24
// コンストラクター
25
public Point(int x0, int y0) {26
x = x0; y = y0;
27
}
28 }

move と distance, print, moveAndPrint はこのクラスのメソッドである。
メソッドの中では、同じオブジェクトの他のフィールド（例えば x, y）やメソッド（例えば move や print）を「.」なしで参照することができる。
さらに各クラスはクラスと同じ名前の特別なメソッド（コンストラクター ）を持つことができる。上の例では Point クラスに int 型の引数 2 つを取るコン
ストラクターを定義している。

詳細: プログラマーがコンストラクターを1つも明示的に定義しないときは、すべてのフィールドに既定値を割り当て、他に何もしない引数なしのコンストラクターが自動的に用意される。

他のメソッドの場合と異なり、コンストラクターの定義のときは戻り値の型は指定しない。

オブジェクト指向言語 第 5 章 p.3

コンストラクターを使うと、Point 型の変数を次のように new というキーワー
ドを使って初期化することができる。
p = new Point(1, 2);
これで、Point クラスのインスタンスが生成され、フィールド x が 1、y が 2に初期化される。
オブジェクトのメソッドにも やはり「.」演算子を用いてアクセスする。次に示
す PointTest は Point クラスをテストするための別のクラスであり、 main
メソッドのみからなる。
ファイル PointTest.java（バージョン 1）
1 public class PointTest {2
public static void main(String args[]) {3
Point p = new Point(10, 20);
4
p.move(1, -1);
5
p.print();
6}
7 }static はメソッドがクラスメソッドであること（他のスタティックでないフィールドやメソッドに依存しないこと）表す修飾子である。クラスメソッドは、Cや C++ の通常の（メソッドではない）関数と同じ感覚で使うことができる。
PointTest はフィールドが一つもない、変なクラスであるが、Java ではすべてのメソッドはクラスの中に宣言しなければならないため、このようなクラスも必要になる。
Q 5.3.1 上記の PointTest クラス（バージョン 1） + Point クラス（バージョン 2）を実行したときの出力を書け。

5.4 継承Point にさらに色の属性を持たせて ColorPoint というクラスを定義する。このとき既存の Point クラスを利用して、増えたフィールドやメソッドだけを定義する。このことを Point クラスを

（インヘリット）する（名詞形は）という。Pointクラスは ColorPoint クラスのである、という。逆に ColorPoint クラスはPointクラスのである。
継承するときは、クラス名の後に「extends」の後に続けてスーパークラスの名前を一つだけ書く。以下のファイルを Point.java と同じディレクトリーに置く。
ファイル ColorPoint.java（バージョン 1）
1 public class ColorPoint extends Point {2
public String color;3
public ColorPoint(int x, int y, String c) {4super(x, y); /* 1 */5color = c;

オブジェクト指向言語 第 5 章 p.4

6
}
7
8
@Override9
public void print() {10
System.out.printf("<font color='%s'>", color); // 色11
System.out.printf("(%d, %d)", x, y);/* 2
12// super.print(); でも可13
System.out.print("</font>");
// 色
14
}
15 }

ColorPoint では、新しいフィールド color と再定義するメソッド
print()、それとコンストラクターのみを定義している。（このように継承を用いると既存のクラスを利用して差だけを記述すれば良い。これまで GUI アプ
リケーションを簡単に作成できたのはスーパークラスの JPanel に必要な処理がほとんどすべて記述されていたからである。）コンストラクターの中のsuper(x, y) という式 (/* 1 */) はスーパークラス (Point) のコンストラク
ターを呼び出す。 super はスーパークラスを表すキーワードである。

詳細: 継承したクラスのコンストラクターでは、最初の文でスーパーク
ラスのコンストラクターを呼び出さなければいけない。（ただし、スーパークラスが引数なしのコンストラクターを持っていて、スーパークラ
スのコンストラクター呼び出しがない場合は、自動的に追加される。）

色は、文字列で表すことにする。print() の中では、HTML のタグを用いて色を変更している。このプログラムの出力結果を HTML ブラウザーで表示すると、実際にその色で文字が表示される。
また、ColorPoint の print() の 2 行目（/* 2 */）は Point の print()と同じなので、単に super.print(); と書くこともできる。この場合、super はスーパークラスを指す。
下のプログラムの main メソッドの 1 行目 (/* 3 */) で ColorPoint クラスのインスタンスが生成される。フィールド x が 10、y が 20、color が “green”にそれぞれ初期化される。また、インスタンスは自分が ColorPoint クラスに属するという情報も持つ
Point からフィールド x と y とメソッド move は継承されるので、引き続き利用することができる (/* 4 */) 。
ファイル PointTest.java（バージョン 2）
1 public class PointTest {2
public static void main(String args[]) {3
ColorPoint cp = new ColorPoint(10, 20, "green"); /*4
cp.move(1, -1);/*
5// cp.color = "noSuchColor";
6
cp.print();
7}
8 }オブジェクト指向言語 第 5 章 p.5

Q 5.4.1 上記の PointTest クラス（バージョン 2）+ ColorPoint（バージョン 1）+ Point（バージョン 2）を実行したときの出力を書け。

Q 5.4.2 DeepPoint クラスは、このプリントで定義されたPoint クラスを継承
し、新しいフィールド int depth を持っている。コンストラクターは x, y,
depth フィールドの初期値を引数とする。 print も再定義されていて、
depth が 5 の DeepPoint は“(((((11, 19)))))”のように括弧が 5 重になって出力される。
DeepPoint クラスの定義を完成させよ。
ファイル DeepPoint.java
1 public class DeepPoint{
2
3// フィールドの定義
45
public DeepPoint(int x, int y, int d) {67depth = d;
8
}
9
10
11
public void print() {12
int i;
13
for (i = 0; i < depth; i++) {14
System.out.print("(");
15}
16
System.out.printf("%d, %d", x, y);17for (i = 0; i < depth; i++) {18
System.out.print(")");
19
}
20
}
21 }

次の PointTest（バージョン 2b）を実行すると “((((10, 20))))”と表示するはずである。
ファイル PointTest.java（バージョン 2）
1 public class PointTest {2
public static void main(String args[]) {3
DeepPoint dp = new DeepPoint(10, 20, 4);4
dp.move(1, -1);
5
dp.print();
6}7 }5.5 動的束縛
次のような例を考える。

オブジェクト指向言語 第 5 章 p.6

PointTest クラスに testPoint という Point を引数として受け取る静的メ
ソッドを用意し、ファイル PointTest.java（バージョン 3）
1 public class PointTest {2
public static void testPoint(Point p) {3
p.move(10, 10);4
p.print();
5}

main メソッドでは、Point, ColorPoint, DeepPoint の 3 つのクラスのインスタンスを生成し、testPoint メソッドに渡す。
ファイル PointTest.java（バージョン 3、続き）
7
public static void main(String args[]) {8Point
p = new Point(1, 2);9ColorPoint cp = new ColorPoint(3, 4, "green");
10
DeepPoint dp = new DeepPoint(5, 6, 5);
11
12
testPoint(p);13testPoint(cp);14
testPoint(dp);
15
}
16 }

testPoint メソッドを呼び出すときに、 ColorPoint, DeepPoint から
Point への型変換（キャスト）が暗黙に行なわれているわけであるが、これはサブクラスからスーパークラスへの型変換（ワイドニング , widening という）であり、一般的にサブクラスの方がスーパークラスよりメソッドが多いので可能である。

詳細: 一般にサブクラスのオブジェクトをスーパークラスの変数に代入することは無条件に可能である。
ファイル CastTest.java
1 public class CastTest {2
3
public static void main(String[] args) {4
ColorPoint cp = new ColorPoint(1, 2, "green")
5
Point p = cp;// ワイドニング
6
p.print();

一方、スーパークラスの型を持つ式をサブクラスを期待するコンテキス
トで使用するためにはキャスト（明示的型変換）が必要である。
ファイル CastTest.java（続き）
8// 次の行をコメントアウトすると実行時エラー
9// p = new Point(3, 4);
10
ColorPoint cp2 = (ColorPoint)p; // ナローイング
11
cp2.color = "red";12
cp2.print();
13
}
14 }

オブジェクト指向言語 第 5 章 p.7

p が指しているオブジェクトが ColorPoint クラス（あるいはそのサ
ブクラス）のインスタンスでないときは実行時に例外
ClassCastException が発生する。

testPoint メソッドの中では何が起こるだろうか? testPoint メソッドの中で呼び出される move メソッドは各クラスで共通なので、同じメソッドが起動される。しかし、print メソッドは ColorPoint では上書きされているので、各クラスで異なるメソッドである。この場合、どのメソッドが起動されるのだ
ろうか?
Q 5.5.1 上記の PointTest（バージョン 3）（Point, ColorPoint,
DeepPoint は、ここまでの最新版）の出力を予想せよ。
1. ⬜ (11, 12)(13, 14)(15, 16)
2. ⬜ (11, 12)<font color='green'>(13, 14)</font>(((((15,16)))))
実は、Java では、各オブジェクトの生成時のクラスの print メソッドが起動されて、のように表示される。

このように、字面（変数の型）によって実行されるコードが決まらずに、変数が参照しているオブジェクトの型によって、呼び出されるメソッドが定まる。
通常、実際に変数が参照するオブジェクトの型は実行時までわからないので、このようなメソッドの振舞いを(dynamic binding) という。

静的 (static) — プログラムを実行する前（コンパイル時）にわかる性質
動的 (dynamic) — プログラムを実行してみないとわからない性質（参考） C++で、上のような Java プログラムを真似て Point,
ColorPoint, DeepPoint の各クラスを定義し、
1
2
3
4
5
6
7
8
9

// …
Point* p = new Point(1, 2);
ColorPoint* cp = new ColorPoint(3, 4, "green");DeepPoint* dp = new DeepPoint(5, 6, 5);
testPoint(p);testPoint(cp);
testPoint(dp);// …

のように書くと、すべて Point クラスのprint メソッドが起動されて、“(11, 12)(13, 14)(15, 16)”のように表示される。
この C++ のプログラムを Java のような振舞いにするためには、print メソ
ッドを(virtual function) というものにする必要がある。仮想関数とは、ポインターの型ではなく、ポインターが参照している実際のオブジェク
ト（上の例では *p, *cp, *dp）の型によって実際に呼び出されるコードが決
まるメソッドのことである。Java のメソッドはすべて仮想関数である。

オブジェクト指向言語 第 5 章 p.8

一方、C++ のメンバー関数を仮想関数にするためには virtual というキーワードを宣言の前につける。
1 class Point { // 注: これは C++ のプログラム
2 public:3
int x, y;4void move(int dx, int dy);
5
virtual void print(void);
6 };C++ では効率を重視するので、非仮想関数をデフォルトにしているのである。
動的束縛はコードの再利用の可能性を高める。例えば、Point クラスに定義されたmoveAndPrint メソッドを考える。
123public void moveAndPrint(int dx, int dy) {print(); move(dx, dy); print();}

moveAndPrint は ColorPoint にも DeepPoint にも適用できて、print メ
ソッドは、それぞれのクラスのものを呼び出してくれる。動的束縛がなければ（静的束縛ならば） moveAndPrint をコンパイルする時点で、既知のクラスはPoint クラスだけだから、move と print は Point クラスのものになる。そうすると、ほとんど同じようなメソッドをクラス毎に定義しなければならない。例えば、print メソッドをオーバーライドすれば、moveAndPrint のような、print を間接的に呼び出すすべてのメソッドをオーバーライドしなければいけない。
グラフィカルユーザーインタフェース (GUI) を用いるアプリケーションでは、ボタン・ラベル・テキストフィールドなどのように、ある面ではほとんど同じだが微妙に異なるというデータ型を扱うことが多い。Java ではこれらの部品に対して移動・拡大/縮小・削除などの操作を同じような方法で行なうことができる。このようなプログラムで、一つのメソッドを多くのデータ型に対して再利用するために、動的束縛は欠かせない機能である。
ポリモルフィズム (polymorphism・多相) — 関数などが様々な型の引数に対して適用できること ⦅本来は、ポリモルフィズムという言葉の意味は、 これだけだ
が、 オブジェクト指向言語の文脈では「ポリモルフィズム」を「動的束縛」 の意味も含めて「関数などが様々な型の引数に対して適用でき、実行時の 型によって振舞いが異なること」として用いることがある。⦆
“Poly”は“多くの”という意味 ⦅ポリエチレン、 ポリゴン（=多角形）、ポリネシ
アなどの“ポリ”と同じ語源⦆、“Morph”は“形”という意味で、1 つの関数がいろい
ろな型（形）に対して適用可能であることを表す。
今まででも継承を用いてサブクラスを定義するときに、スーパークラスに対して定義されていたメソッドを、そのまま何気なくサブクラスにも適用していた。このようなことが可能なのも、ポリモルフィズムがサポートされているからである。

オブジェクト指向言語 第 5 章 p.9

例えば、JButton, JLabel, JTextField, JTextAreaなどの GUI 部品はすべて
JComponent （正確には javax.swing.JComponent）のサブクラスである。
だから、どの部品も JComponent のメソッドである setVisible,
setEnabled, setLocation などを持っている。次のような例を試してみよう。
例題 5.5.2 ファイル HideShow.java
1 import javax.swing.*;
2 import java.awt.*;
3 import java.awt.event.*;
4
5 public class HideShow extends JPanel implements ActionLi
6private JTextField input;
7
private JLabel lbl;
8
private JButton b1, b2;
910
public HideShow() {11
setPreferredSize(new Dimension(300, 50));12lbl = new JLabel("label");13
input = new JTextField("text", 5);14
b1 = new JButton("Hide");15
b2 = new JButton("Show");16
b1.addActionListener(this);
17b2.addActionListener(this);18
setLayout(new FlowLayout());
19
add(lbl); add(input); add(b1); add(b2);
20
}
21
22
public void actionPerformed(ActionEvent e) {23
if (e.getSource() == b1) {24
lbl.setVisible(false); input.setVisible(fals25} else if (e.getSource() == b2) {26
lbl.setVisible(true); input.setVisible(true
27
}
28
}
29
30
public static void main(String[] args) { /* 省略 */31 }“Hide”ボタンを押した状態

最初の状態

どの型の部品も setVisible メソッドに同じように反応している。これらはすべて JComponent 型の変数に代入できるし、JComponent 型の引数を取るメソッド（例えば add など）に同じように渡すことができる。また、配列などにこのクラスのサブクラスを詰め込んで、一斉にメッセージを送る（つまり、メソ
ッドを起動する）ことなどもできる。

オブジェクト指向言語 第 5 章 p.10

しかし、これらのクラスはフィールドの種類や数も異なるし、それにともなって、 setVisible などのメソッドのそれぞれのクラスでの実装も少しずつ異なるかもしれない。（setVisible メソッドの実装自体は同一かもしれないが、一般的にはそこから間接的に呼び出されるメソッド の実装は異なる。） これもポリモルフィズム（動的束縛）の一例である。もし動的束縛がなければ（静的
束縛ならば）コンパイル時にクラスが固定されてしまうため、ほとんど同じようなメソッドをクラス毎に定義する必要がある。例えば、repaint メソッドも
paintComponent メソッドをオーバーライドするたびに再定義する必要が出てくる。

5.6 多重定義（オーバーローディング）
詳細: 動的束縛と混同しやすい概念として多重定義 （オーバーロード ）というものがある。多重定義とは、引数の型や数の異なる同じ名前のメ
ソッドを定義することである。
ファイル OverloadTest.java
1 public class OverloadTest {2
double x, y;
3
4public OverloadTest(double x0, double y0) {5x = x0; y = y0;6}78
public void foo(double dx, double dy) { // foo-1
9x += dx; y += dy;
10
}
11
12
public void foo(int dx, int dy) {// foo-213x *= dx; y *= dy;
14
}

28
29
30
31
32
33
34
35
36
37
38
39

public void print() {System.out.printf("(%g, %g)", x, y);System.out.println();}/* 1 */
public static void main(String[] args) {OverloadTest o = new OverloadTest(1.1, 2.2);o.foo(3.3, 4.4); // foo-1 が呼ばれる
o.print();o.foo(2, 3);// foo-2 が呼ばれる
o.print();/* 2 */

45}
46 }注意: 通常、多重定義は同じような動作をする関数に使用する。このプロ
グラムは多重定義の使い方としては悪い例である。
Q 5.6.1 OverloadTest.java の出力を予測せよ。

オブジェクト指向言語 第 5 章 p.11

動的束縛と決定的に異なる点は、多重定義は静的に（つまりコンパイル時に）解決されてしまうことである。
これは、さらに次のようなメソッドを /* 1 */ の部分に定義するとはっきりする。
ファイル OverloadTest.java（barメソッドの追加）
15
16
17
18
19
20
21
22
23
24
25
26
27

public void bar(Point p) {System.out.print("Point class: ");p.print();
System.out.println();}// bar-1

public void bar(ColorPoint p) {// bar-2
System.out.print("ColorPoint class: ");p.print();
System.out.println();}

ファイル OverloadTest.java（/* 2 */ の部分に追加）
40
41
42
43
44

ColorPoint cp = new ColorPoint(0, 0, "red");Point p = cp;
o.bar(cp);// bar-2 が呼ばれる
o.bar(p);// bar-1 が呼ばれる

Q 5.6.2 OverloadTest.java （/* 1 */, /* 2 */ の部分に追加後）の出
力の追加部分を予測せよ。

つまり Java では動的束縛が起こるのは「.」演算子の前のパラメーターに限られるのである。
多重定義も広い意味では多相（ポリモルフィズム）の一種だが、オブジェクト指向言語に特有のものではない。例えば C 言語でも 「+」「*」な
どの算術演算子は多重定義されている。

5.7 カプセル化
ところで、ColorPoint クラスの color フィールドは、"red", "green" な
ど、色を表す文字列専用で、それ以外が設定されると困るので、専用の設定メ
ソッドを設けて、正当な色を表しているかをチェックしたい。 このため 2 つのメソッド setColor と getColor をColorPoint に 追加する。具体的には、色は "black", "red", "green", "yellow","blue", "magenta", "cyan","white" のいずれかの文字列で指定することにする。このようにフィールドの値を制限したいというのは、他のクラスでもよく起こりうる状況である。

オブジェクト指向言語 第 5 章 p.12

文字列同士が同じ文字列がどうかを判定するには String クラスのというメソッドを用いる。String クラスに対する == 演算子は物理的に同じオブジェクトかどうかを判定するので、 == の結果が true にならなくても、
equals の結果が true になることがある。
java.lang.String クラス
public boolean equals(Object s)
この文字列と指定されたオブジェクトを比較する。
public boolean equalsIgnoreCase(String s)
この文字列と指定された文字列を比較する。大文字小文字を区別しない。
ファイル ColorPoint.java（バージョン 2）
1 public class ColorPoint extends Point {2
public String[] cs = {"black", "red", "green", "yellow
3"blue", "magenta", "cyan", "whit4public String color;
56@Override7
public void print() {8System.out.printf("<font color='%s'>", getColor());
9
System.out.printf("(%d, %d)", x, y); // super.print
10
System.out.print("</font>");// 色を戻11}12
13
public void setColor(String c) {14
int i;
15
for (i = 0; i < cs.length; i++) {16
if (c.equals(cs[i])) {17
color = c; return;
18
}
19
}
20
// 対応する色がなかったら何もしない。
21
}
22
23
public ColorPoint(int x, int y, String c) {24super(x, y);25
setColor(c);26
if (color == null) color = "black";
27
}
28
29
public String getColor() {30
return color;
31
}
32 }

ところで、せっかく setColor と getColor を定義したのだから、フィールドの color は直接、他のオブジェクトのメソッドやクラスメソッドからは見えないようにして、有効な色名以外の値を設定できないようにしたい。（つまり、cp.color = "NoSuchColor"; のような操作ができないようにしたい。）同
じオブジェクトのメソッドからは見えるが、他のオブジェクトのメソッドやク
ラスメソッドからは見えないフィールドやメソッドをであるとい

オブジェクト指向言語 第 5 章 p.13

う。逆に他のオブジェクトのメソッド（あるいはクラスメソッド）からでも見えるフィールドやメソッドをパブリックであるという。プライベートなフィー
ルドやメソッドを定義するためには、public の代わりにという修飾子を使う。color フィールドをプライベートにするために ColorPoint の定義を次のように書き換える。
…
private String color;…

// …

これで color はプライベートなフィールドになる。（ついでに cs もプライベート（かつスタティック）にしておくと良い。） 他のクラスやインスタンスのメソッド（例えば PointTest クラスの main メソッド）で、例えば
cp.color = "NoSuchColor"; のように、このフィールドへの直接操作を行
なおうとするとコンパイル時にエラーになる。
Q 5.7.1 実際にどのようなエラーメッセージが出力されるか確かめよ。

その他のフィールドやメソッドは public という修飾子があるのでパブリックである。

詳細: また、protected という修飾子がつく場合も、private,public, protected の、どの指定もない場合もある。これをパッケー
ジプライベート (package private) ということもある。この場合の意味
は public に近いが、プログラムをいくつかのファイルに分割した場
合には意味が変わってくる。これらの修飾子の説明は後述する。

このように、クラスを構成するフィールドやメソッドの一部を同じクラスのメ
ソッド以外に非公開にすることをあるいは(encapsulation)という。カプセル化を行なっておくと、メソッド以外のプログラムがクラスの実装の詳細に依存していないことが保証できるので、クラスの実装の変更が容易に行なえるようになる。（例えば ColorPoint クラスの場合、color フィー
ルドは "black", "red" などの文字列の配列 cs中の添字を記憶するように変更することも可能である。）
関数・サブルーチンを利用する場合、外部から見た振舞いが同じである限り、内部でどのように実現されていても構わない。例えば、配列の要素を大きさの順に並び替える（ソーティング）方法はいくつもあり、（性能に違いはあるかもしれないが）自由に入れ換えることができる。これと同じように、クラスを利用する場合でも、2つのクラスの内部の実現方法が少々異なっていても、外部から見た振舞いが同じであれば、それらを入れ換えることができる。カプセル化は、そのためにクラスの内部の実現方法を外部から隠すことを意味する。
クラスを設計するとき、外部から使用する必要のないメソッドやフィールドはprivate と宣言して隠蔽するべきである。ただし、何でもかんでも privateとすれば良いというわけでもないことに注意する。外部から必要な操作ができオブジェクト指向言語 第 5 章 p.14

ないクラスを設計してしまえば、ソースのコピーという最悪のかたちの再利用をせざるを得なくなってしまうからである。

詳細: public, private, protected などの修飾子によるアクセス制限
は、Java の一番最初からあった仕組みだが、これだけでは仕組みとして十分でないことがだんだんわかってきた。このため Java 9 より
Jigsaw の名前で知られるモジュールシステムが導入された。ここではJigsaw の説明は割愛する。
問 5.7.2 color フィールドが、各色に対応する配列 cs 中の要素の添字（int型）で表すように ColorPoint の実装を変更せよ ⦅実 際のプログラムでは、このように記憶領域をケチる必要がある場合はほとん どない。ここで、color フィールドを int 型に変えるのは、単なる説明のためである。⦆
問 5.7.3 DeepPoint クラスに depth が 1 〜 10 の値に制限されるように設定するメソッドvoid setDepth(int d)（および depth を読み出すメソッド
int getDepth()）を定義せよ。（setDepth メソッドに 0 以下または 11 以
上の値が引数として渡されたときはそれぞれ 1 または 10 になるようにせよ。
また、コンストラクターに depth の初期値として、 0 以下または 11 以上の値が引数として渡されたときも 1 または 10 になるようにせよ。）そして depth
フィールドの値は他のクラスのオブジェクトからは setDepth メソッドを通じてのみ変更できるようにせよ。
問 5.7.4 SecretPoint クラスは、、このプリントで定義された Point クラスを継承し、2つの新しいフィールド int a, b を持っている。この2つのフィー
ルドはコンストラクター内で乱数により初期化される。print メソッドも再定義されていて、方程式 a ⋅ x + b ⋅ y = 1 を満たすときだけ、普通に (1, 2) のように出力し、方程式を満たさないときは、(?, ?) とクエスチョンマークを出力する。SecretPoint クラスを定義せよ。ただし、フィールド a, b はprint メソッド以外の方法で他のクラスのオブジェクトから値が見えないようにせよ。

5.8 総称クラスの定義
総称クラス （型パラメーターを持つクラス）を定義するときはクラス名の後に<と>で囲って型パラメーターを書く。この型パラメーターはフィールドやメソッドの型の中で使用することができる。
Pair クラスでは E1, E2 が型パラメーターである。
ファイル Pair.java
1 public class Pair<E1, E2> {2
public E1 fst;3
public E2 snd;
4
public Pair(E1 f, E2 s) {5
fst = f; snd = s;6}
7 }オブジェクト指向言語 第 5 章 p.15

ファイル Triple.java
1 public class Triple<E1, E2, E3> extends Pair<E1, E2> {2
public E3 thd;
3
public Triple(E1 f, E2 s, E3 t) {4super(f, s);5
thd = t;6}
7 }ファイル TripleTest.java
1 public class TripleTest {2
public static void main(String[] args) {3
Triple<Integer, String, Double> test = new Tripl
4
System.out.printf("(%d, %s, %g)%n", test.fst, te
5}6 }5.9 章末問題
問 5.9.1 ゲームのクラス階層を設計するために、テスト用のいくつかのクラスを作成した。まず、Player クラスは味方キャラクターを表すクラスである。
さらに、敵キャラクターのためにいくつかのクラスがある。Enemy クラスはす
べての敵キャラクターのスーパークラスとなるクラスである。 GenericEnemy
クラスと Boss クラスは Enemy クラスを継承し、さらに、Alice クラスとGrace クラスは、 GenericEnemy クラスを継承する。なお、これらのクラス
名には特に意味はない。
Enemy クラスは attack メソッドを持ち、 GenericEnemy クラスは
updateDamage メソッドを持つ。
ファイル Player.java
1 public class Player {2private int hp;
3
4
public Player(int initHp) {5
hp = initHp;6}78public void damage(int damage) {9
hp -= damage;
10
System.out.println(damage + " のダメージ、残り HP
11
}
12 }

ファイル Enemy.java
1 public class Enemy {2public void attack(Player p) {}3 }ファイル GenericEnemy.java

オブジェクト指向言語 第 5 章 p.16

1 public class GenericEnemy
2
private String name;3
public int damage;
4
5
public GenericEnemy(String n, int d) {6
name = n;7damage = d;8}910@Override11
public void attack(Player p) {12
System.out.print(name + " の攻撃: ");13
p.damage(damage);
14
updateDamage();
15
}
16
17
public void updateDamage() {}18 }{ファイル Alice.java
1
2 public class Alice{3
private int init;
4
5
public Alice(int d) {6
super("Alice", d);7
init = d;8}910@Override11
public void updateDamage() {12damage += init;// エラーにならない13// name += "!";// エラーになるのでコメントアウト
14
}
15 }

ファイル Grace.java
1 public class Grace{2
private int ratio;
3
4
public Grace(int d, int r) {5super("Grace", d);6ratio = r;
7}89@Override10
public void updateDamage() {11damage *= ratio; // エラーにならない12// name += "?";// エラーになるのでコメントアウト
13
}
14 }

ファイル Boss.java

オブジェクト指向言語 第 5 章 p.17

1 public class Boss2
public int count;3
4
public Boss() {5
count = 0;6}78@Override9
public void attack(Player p) {10
System.out.print("Boss の攻撃: ");11
if (++count >= 3) {12
p.damage(1000);
13} else {14
p.damage(0);
15
}
16
}
17 }

{

さらに、次のような main メソッドを持つテスト用プログラム Main.java を定義する。
ファイル Main.java
1 import java.util.ArrayList;
2
3 public class Main {4public static void main(String[] args) {5
ArrayList<Enemy> enemies = new ArrayList<>(); /6Player p = new Player(3000);
7
8
Alice a = new Alice(100);
9enemies.add(a);10Grace g = new Grace(100, 2);11enemies.add(g);12Boss b = new Boss();13
enemies.add(b);1415
for (int i = 0; i < 3; i++) {16
for (int j = 0; j < enemies.size(); j++) {17
Enemy e = enemies.get(j);18e.attack(p);
19
}
20
}
21
}
22 }

空欄を埋めてこれらのクラスの定義を完成させよ。さらに、Main クラスのmain メソッドを実行するとき、出力はどうなるか？

オブジェクト指向言語 第 5 章 p.18

キーワードオブジェクト指向、クラス、フィールド（メンバー変数）、メソッド（メンバー関数）、インスタンス、継承（インヘリタンス）、スーパークラス、サブク
ラス、プライベートメンバー、パブリックメンバー、情報隠蔽、カプセル化、
ポリモルフィズム、動的束縛、多重定義

オブジェクト指向言語 第 5 章 p.19