クラスとは何かを集合論から考えなおす

いま論理学とか集合論とかを勉強してるので、クラスって何かという話のときも集合論として考えてた。
なので、そのとき考えたことをまとめておく。


まず、「なにか」があるとする。「なにか」はなんでもいい。
そして、その「なにか」の集合を考える。たとえば"aaa"や"bbb"を要素とするStringという集合を考える。3とか5とかを要素とするIntegerという集合を考える。

ここで、その集合がなにを扱うかという規則をどうするかというのがオブジェクト指向としては大切になるかもしれないけど、今回はそこには触れないでおく。
とにかく、「なにか」と『「なにか」の集合』を考えることをここでは大切にする。


これで『「なにか」の集合』を考えたところで、『「なにか」の集合』を要素とする集合を考えて、この集合をクラスとする。つまり、クラスという集合は、StringやIntegerといった集合を要素にもつ。


ここで、クラスという集合を考えるときに、"aaa"や5とかを要素としてもつ状態のままStringやIntegerをぐるっとまとめてはいけない。

このように書くと、"aaa"や5とかもクラスの要素で、StringやIntegerはクラスの部分集合ということになってしまう。"aaa"や5はクラスの要素ではなく、クラスの要素の要素になる。クラスの要素となるのはStringやIntegerである。


さっきのように書くならば、集合の要素になる全ての「なにか」を要素とするObjectという集合を考えて、こういう図を描く。

つまり、StringやIntegerはObjectの部分集合で、"aaa"はStringという集合の要素でもありObjectという集合の要素でもあるということになる。71はIntegerという集合の要素でもあり、Objectという集合の要素でもある。
そうすると、StringのObjectの一種であるという関係になって、この関係は継承関係ということになる。


IntegerはNumberを継承するとして、ほかにFloatという3.14とか1.41とかを要素とする集合を考えてこれもNumberを継承するということをあらわすと、このようになる。

FloatとIntegerはNumberを継承するので、FloatとIntegerはNumberの部分集合になっている。


話は戻って、クラスとStringやIntegerと"aaa"や71を同時に書こうとするとこんな感じになる。"aaa"や71という「なにか」はクラスという集合とは別の軸に存在するという感じ。


ここで続く話をやりやすくするために、集合の階層という考え方を導入してみる。
集合ではない要素の階層を階層0とする。今回は「なにか」が階層0になる。"aaa"や71などは階層0に属する。
そして、集合の階層を階層1とする。StringやIntegerは階層1に属する。
最後に、集合の集合を階層2とする。つまり、ここではクラスが階層2になる。
ここでは出てないけど、集合の集合の集合を階層3としておく。集合の集合の集合の集合を階層4、集合の集合の集合の集合の集合を階層5・・・と続く。


現実問題として、実行時に扱えるのは、階層0の要素になっている「なにか」である。階層0の要素はオブジェクトとしてメモリに配置されて処理される。
そして、プログラム中に記述できるのは、階層1の要素になっている『「なにか」の集合』である。これは型として扱える。階層0の要素は、特別なものだけがプログラム中に記述できる。つまり、リテラルとして言語仕様で決められたものだけが、プログラム中に記述できるということだ。


そして、ここからが問題なのだけど、このクラスの集合に、クラスという集合自体も含めてみる。


そうすると、こんな感じで、クラスの中にクラスがあってその中にクラスがあって、ということになる。


つまり、階層0や階層1の要素と同列にクラスがあることになる。それどころか、いままでは階層が2までしかなかったのに、クラスはクラスを要素としてもつとした瞬間、無限の階層が発生してしまう。
それと、なんだかとばっちりを受けたような形で、StringやIntegerも階層0の要素のようになってしまった。


これだと、「クラス」と言ったときに階層0にあるクラスか階層1にあるクラスか、それとも階層2のクラスかわからない。StringやIntegerも階層1のものか階層0のものかわからなくなってしまった。
これが、クラスとは何か - SiroKuro Pageのような混乱につながっているのだと思う。
けれども、階層0にStringやIntegerが来たことで、プログラムの実行時にStringやIntegerの処理が可能になった。プログラムで処理できるものは階層0のものだけだからだ。つまり、実用上クラスの要素としてクラスをもつというのは重要なこととなる。


そこで、話がややこしいしクラスという概念自体が実際に処理できるわけではないので、実装上の区別として、それぞれの階層のクラスに別の名前をつける。
階層1の要素としてのクラスをClassとする。
そして、階層1の要素を階層0の要素として扱うときには「.class」を付けることにする。つまり、Stringを階層0で扱うときはString.class、Integerを階層0で扱うときはInteger.classとするということだ。クラスを階層0で扱うときはClass.classになる。


こうして、クラスが要素としてクラス自身を持つ代わりにClassをもつこととなり、無限に集合の要素をたどれることはなくなったし、クラスを実装して実行時に処理することも可能になった。めでたしめでたし。というわけにはいかず、各階層の「クラス」に何か名前が欲しくなるのだけど、何の説明もなく通じる用語は決まってないような気がする。上で挙げたSiroKuroさんのところでは、階層0のクラスを「クラス」、階層2のクラスを「クラス型」と呼び、階層1のクラスは「クラス名」としているということになるのかな。


ちなみにJavaにおいて式「Class.class instanceof Class」はtrueとなるけど、「Integer.class instanceof Integer」はtrueにならない。つまり、「クラスはクラスであるけど、整数は整数ではない」ということをプログラム的にすっきり表せた。「クラス」はクラスのインスタンスなのだけど、「整数」は整数のインスタンスではないのだ。「整数」というのは小数点以下を考えないような数を表す概念であって、これ自体は数ではないので、「整数」は整数ではない。
実は「Integer.class instanceof Integer」はfalseにもならないのだけど、実際どうなるかは試してください。