0. オブジェクト指向という「新しい概念」
プログラミングの初心者で、今までC言語を使ったコーディングの経験しかない人にJavaを教えるのは、思いのほか難しい。(情報系の大学を想定。1年次にC、2年次にJavaをやる場合が多いそう。)
「クラスとは何か」と聞かれたら「オブジェクト指向で使う」と説明するが、
「ではオブジェクト指向とは何か」と聞かれたらどうだろう。
「世の中にはいろんなモノゴトがある。そしてモノゴトには状態と振る舞いがある。
だからいろんなモノゴトに対して、状態と振る舞いを集めてクラスの中にコーディングしていく。これがオブジェクト指向だ」
と答えて、相手は納得するかもしれない。
しかし、この後に続く「カプセル化」、「継承」の説明を聞いて、相手は混乱するかもしれない。
いきなり「状態と振る舞いを集めてモノゴトを表現する」と教わったかと思えば、次に「状態を振る舞いに閉じ込める」、「具体的なモノゴトは抽象的なモノゴトをケイショウする」と教わるなど、覚えることが多い。「理論より実践」な人にとっては『苦痛』だろう。
こうして、オブジェクト指向によるコーディングに苦手意識を持ち、プログラミング界隈から離れていく人が、後を絶たないようである。
『苦痛』の正体は、「オブジェクト指向とは、つまり何か」が一言で説明されないことにある。
憶測だが、人は新しい概念を、既存の概念になぞらえて考え、誤差の部分は実践により覚える。
天空の城ラピュタで、空に浮かぶ伝説の島ラピュタの発見のために、主人公パズーとシータが政府専用機ゴリアテを尾行するくだりで、一度ゴリアテを見失いそうになる。
そのとき、協力者ドーラは「見張り台」と呼ばれる凧を使って追跡の続行を試みる。
このときパズーは次のよう指示される。
(凧の)操縦は体で覚えるんだ!
命懸けの無茶ぶりを、難なくこなすパズーの勇敢さが引き立つシーンではあるが、
ドーラも何も考えずにパズーに指示したのではない。ゴリアテ尾行の前、半ば脅されて政府に連行されたシータを救出すべく、ドーラはパズーを乗せてシータの居場所へと小型船を出した。この際、事故の衝撃でドーラは気を失い、小型船は危うく墜落するところであったが、パズーが操縦を急遽代行したことで、九死に一生を得たのだ。
凧の操縦(新しい概念)の指示は、極度に緊迫した状況で小型船を妥当に操縦(既存の概念)したパズーの実績を見込んでのことだろう。
オブジェクト指向も、同じことではないだろうか。
皆が知っているような何かでオブジェクト指向をたとえ、違いの部分は実践をもとに各々が学習していけばそれでよい。少なくともイメージをつかめずに、手を動かすことすらできないよりはよっぽど。
本記事ではこのようなアプローチでJavaによるプログラミング、およびオブジェクト指向を解説していく。
最後に補足すると、「プログラムとは何か」、「コンピュータ(計算機でもよい)とは何か」を定義に立ち返って考えれば、
クラスが「コンピュータを構成できるプログラム」であること、インスタンスがコンピュータであることは論理的に証明可能である。
1. Javaとは「『仮想コンピュータ』を作りまくる言語」だ
クラスを一言で説明するなら「コンピュータを構成できるプログラム」である。
このことは論理的に証明できる。
同じように、インスタンスが「仮想コンピュータ」であることも証明可能だ。
1-1. クラスが「コンピュータを構成できるプログラム」であることの証明
まず、ベーム・ヤコピーニの定理[1]にあるプログラムの3要素
「順次」、「反復」、「分岐」を、クラス内のメソッドですべて行える。
ということはもちろん、メソッドもプログラムといってよいのであるが、
残念なことにメソッドは「記憶領域」を持たない。
(蛇足:メソッド内でのみ有効な「ローカル変数」は、メソッド終了時に寿命が尽きるので、「記憶」したとは言えない)
そのためチューリングマシン[2]における「ヘッダ」を作ることができないため、
メソッドはプログラムにはなれても、コンピュータにはなれないのだ。
その点、クラスであれば「フィールド」(java以外の言語なら「プロパティ」などと呼ぶ)を持っていて、これを記憶領域として使うことができるため、
クラスはコンピュータを構築することが可能なのだ。(証明終わり)
別の言い方をするならば、クラスとはコンピュータの設計図だ。
1-2. インスタンスが「仮想コンピュータ」であることの証明
クラスそのものは、インスタンスという「実体」を作るための型である。
クラスという設計図をもとにインスタンスが生成されるのであるから、
インスタンスは、「クラスという設計図をもとに作った何か」である。
1-1節で証明したことをここに適用すると、
インスタンスとは、
「『コンピュータの設計図』という設計図をもとに作った何か」
である。それは(設計図通りに作れば)コンピュータである。(証明終わり)
2. OSと仮想コンピュータを作って遊ぼう
第1章のことより、クラスとは「コンピュータを構成できるプログラム」であり。インスタンスとは「コンピュータ」のことであることが分かった。
「コンピュータを構成できるプログラム」とは、まさにOSである。
不思議かもしれないが、javaでプログラミングするたびに、プログラマはOSを作っているし、new
するたびに仮想コンピュータを作っているのである。
例えば、次のコードを考えよう。
public class Mindows
{
private byte[] ram;
public byte getRam(int address){return this.ram[address];}
public void setRam(int address, byte value){this.ram[address]=value;}
public Mindows(int ram_size){this.ram = new byte[ram_size];}
}
class Main{ public static void main(String...args)
{
Mindows computer = new Mindows(64);//64byteのRAMを持つコンピュータの作成
int i=0;
for(char each_char : "hello world".toCharArray())
computer.setRam(i++, (byte)each_char);
//0~10番地に「hello world」を書き込む
for(int j=0; j<11; j++)
System.out.print((char)computer.getRam(j));
//0~10番地を(charとして)表示
//→コンソールに「hello world」と表示される。
}}
このコードではMindows OSを定義し、このOSの入った仮想コンピュータcomputer
を作って、
RAMを操作している。computer
(インスタンス)を使ってできることを定義しているのはOS(クラス)であるが、OSが直接何かするわけではなく、実際に動作する実体はcomputer
(インスタンス)の方である。
この他にも、
final定数フィールドでROMを作ったり、
finalメソッドでBIOSを作ったり、
ファイル入出力の機能を使ってストレージを持たせたりすることなどが考えられる。
3. javaの「実際」に当てはめて考える
3-1. ArrayListクラス
ArrayList<T>
型インスタンスは記憶領域に順番付けてT
型オブジェクトを格納しておくことのできる仮想コンピュータである。
3-2. Listインタフェース
List<T>
インタフェースは、記憶領域にT
型オブジェクトを記憶しておくプログラムの「宣言」に過ぎない。記憶領域の使い方を「おおよそ」決めるものといってよいだろう。
ArrayList<T>
やLinkedList<T>
は、このインタフェースを「実装」している。
この事実は、ArrayList<T>
やLinkedList<T>
が「記憶領域を、T
型オブジェクトの格納に使う」と宣言していることを意味する。
インタフェースを実装した有言...クラスは、インタフェースに定義された空っぽのメソッドをオーバロードし、具体的に定義しなくてはならない。...実行
インターフェースによる宣言は、家電を想像すれば理解しやすい。炊飯器や洗濯機(や、いまの時代では、掃除機も)は、どれもコンピュータといってよいだろう。
炊飯器はコメや水の重さから、炊き上がりの米の硬さや残り時間などを計算してくれる。洗濯機も、自動で判断する場面がある。掃除機は、ルンバをみれば分かるだろう。
今から作るクラス(プログラム)が炊飯器なのか洗濯機なのかはたまた掃除機なのかをはっきり言っておくことは重要だ。また、場合によっては「特定のインタフェースを実装したクラスにだけ、特別な使い方を認める」といったことも出来る。(例えば例外クラスなどがそれである。)
3-3. カプセル化
「コンピュータ内部の回路をお客様にお見せしたくない」―それがカプセル化だ。
第2章に示したコードをもう一度見てみよう。
computer
のRAMに書き込むとき、わざわざsetRam(アドレス, 書き込む値)
というメソッドを呼んでいる。
そんなことをしなくても、ram[アドレス]=書き込む値
ではダメなのだろうか。
もちろん、原理的にはそれで問題ないが、実際には、やりにくくなっている。
「Mindows」開発者は、メモリへの書き込み機能としてsetRam(アドレス, 書き込む値)
を作ってはいるが、メモリそのものを剥き出しにはしていないのだ。実際のコンピュータが、メモリを鉄の内側に閉じ込めているように、javaのコード上でprivate
というアクセス修飾子を付けている。
このようにすることで、記憶領域をユーザに勝手にいじられないようにして、誤作動の可能性を低減させている。
4. OSとコンピュータが自由に作れれば、オブジェクト指向プログラミングが可能
残念ながらOSをクラスとみることは必ずしもできない。
クラスの持たない概念を持つOSが存在する(というか、ほとんどだ)からだ。
(例: マルチタスクOS、仮想メモリなど。またクロックやCPUを考えなければならない場合は、これをクラスで(実用的に)表現するのは困難であろう)
そこで、クラスが表現できるOSを「クラスOS」、インスタンスが表現できるコンピュータを「インスタンスコンピュータ」あるいは「クラスOSのコンピュータ」と呼ぶことにしよう。
逆に、クラスの持つ概念(フィールド、メソッドのみとする)のどれかを持たないOSは存在しない。(クラスがOSとみなせることの証明から、明らかである)
OSを自由に設計し、そのOSを入れた仮想コンピュータを生成・利用できる環境では、オブジェクト指向による開発が少なくとも部分的に可能である。(「半オブジェクト指向定理」とでも呼ぼう)
これは、すべてのOSがクラスの概念をすべて持っているため、当然のことである。
エラー発生時には、どのコンピュータ(OS)に不具合が生じたかを確認すればよいためエラーの特定が容易であるし、また仕様変更にも柔軟に対応する。
但し、アクセス制限の仕組みがなければカプセル化は利用できず、コピーの仕組みがなければ継承は不可能だ。
n.参考
[1]http://s150001.skr.u-ryukyu.ac.jp/lectures/index.php?ProgrammingAbility
[2]http://e-words.jp/w/%E3%83%81%E3%83%A5%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E3%83%9E%E3%82%B7%E3%83%B3.html