Java入門|多重継承の制限とObjectクラス(すべてのクラスの共通の親)

継承は自由に広がるようでいて、実はしっかりしたルールの上に成り立っている。
多重継承の制限と Objectクラスを知ると、Java のクラス構造が一本の系譜としてきれいに見えてくる。

Java の継承を学んでいると、親クラスから子クラスへ機能を受け継ぎながら、クラスを少しずつ発展させていけることがわかってきます。
このしくみはとても便利で、ドラゴンボールでたとえるなら、共通の戦士の型を土台にしながら、それぞれの戦士に個性を持たせていく感覚に近いです。

ただし、Java の継承には大切なルールがあります。
それは、1つのクラスが同時に複数のスーパークラスを持つことはできない、ということです。つまり、クラスの継承は自由に枝分かれできても、1つの子クラスが2人の親クラスを同時に持つような形にはできません。

そして、もうひとつ見逃せない大事なルールがあります。
それが、スーパークラスを明示しないクラスは、Objectクラスを親に持つというしくみです。これによって、Java のすべてのクラスは最終的に Objectクラスにつながっています。Objectクラスは、まさにすべてのクラスの共通の親です。

今回は、この2つを中心に見ていきます。

  • Java で多重継承ができない理由
  • その代わりにインターフェイスがどう関係するのか
  • Objectクラスがなぜすべてのクラスの共通の親なのか
  • Objectクラスの代表的なメソッドにはどんな意味があるのか

このあたりを、ドラゴンボールの戦士たちの系譜に置きかえながら、やさしく整理していきます。

Java では複数のスーパークラスを同時に継承できない

まず押さえておきたいのが、Java では 1 つのクラスが複数のスーパークラスを同時に継承することはできない、という点です。
これは Java の継承の基本ルールのひとつです。

たとえば、ドラゴンボール風に考えて、

  • Saiyan(サイヤ人)クラス
  • Namekian(ナメック星人)クラス

という2つのスーパークラスがあったとして、その両方を同時に親として持つ 1 つのクラスは作れません。

つまり、こんな考え方はできません。

  • サイヤ人クラスを親にする
  • 同時にナメック星人クラスも親にする
  • その両方のクラスのメンバを、1つのサブクラスがそのまま受け継ぐ

Java では、このようなクラスの多重継承は許されていません。

なぜ多重継承を制限しているのか

本文では細かな技術理由までは踏み込んでいませんが、学習の流れとして大切なのは、Java が継承関係をわかりやすく保つために、親クラスを 1 つに絞っていると考えることです。

ドラゴンボールでたとえると、1 人の戦士が

  • サイヤ人の戦士の型
  • ナメック星人の戦士の型

を、まったく同じ重みで同時に親として持ってしまうと、どのルールを優先すればよいのかがあいまいになります。

たとえば両方の親クラスに同じ名前のメソッドがあったら、

  • どちらの技を使うのか
  • どちらの定義が優先されるのか
  • どちらのフィールドを基準にするのか

がわかりにくくなってしまいます。

Java はそうした複雑さを避けるために、クラスの継承は 1 本の親だけにしています。
そのかわり、階層は下へどんどん伸ばしていけます。つまり、

  • 親から子へ
  • その子からさらに孫へ

という形で、縦に整理された継承関係を作れるわけです。

それでも複数の能力を持たせたいときはどうするのか

ここで出てくるのがインターフェイスです。
Java では、複数のスーパークラスを同時に継承することはできませんが、その代わりにインターフェイスによって、能力の約束を複数持たせることができます。継承はインターフェイスによって擬似的に行うことができます。

ドラゴンボールで考えると、親となる種族や基本の型は 1 つでも、

  • 飛べる
  • 気を使える
  • 感知できる

のような能力の約束は、複数あわせ持てるイメージです。

ここでは詳しい仕組みまでは立ち入りませんが、整理するとこうなります。

しくみできること
クラスの継承親クラスは1つだけ
インターフェイス複数の能力の約束を組み合わせられる

つまり Java は、クラスの多重継承は避けつつ、必要な柔軟性はインターフェイスで補う形になっています。

スーパークラスを指定しないクラスはどうなるのか

ここからが今回のもうひとつの中心です。

Java では、クラスを宣言するときにスーパークラスを指定しなかった場合、そのクラスは自動的に Objectクラスをスーパークラスに持つことになります。

たとえば、

class Saiyan
{
    ...
}

のように書いた場合、見た目には親クラスを書いていません。
でも Java のルールとしては、このクラスは Objectクラスのサブクラスです。

つまり、スーパークラスを何も指定しないクラスは、Objectクラスにつながっているわけです。

Objectクラスはなぜ「すべてのクラスの共通の親」なのか

このルールがあるため、Java のクラスは最終的にすべて Objectクラスにつながります。
だから Objectクラスは、すべてのクラスの共通の親と呼ばれます。

ドラゴンボールでたとえるなら、Objectクラスは、あらゆる戦士クラスのさらに上にある「最も基本の共通土台」です。

  • サイヤ人クラスも、たどっていけば Object に行き着く
  • エリートサイヤ人クラスも、たどっていけば Object に行き着く
  • ほかの別系統の戦士クラスも、たどっていけば Object に行き着く

見た目は別々の戦士でも、もっと上まで系譜をたどると、みんな同じ共通の親を持っているわけです。

この考え方があるから、Java のオブジェクトは「全部バラバラの存在」ではなく、「共通の基本ルールを持つ仲間」として扱えます。

継承の階層はどうつながっていくのか

クラスの継承関係は、上から下へと連なっていきます。
たとえば、ドラゴンボール風に整理すると、こんな階層が考えられます。

階層
すべてのクラスの共通の親Object
その下の基本クラスSaiyan
さらにその下の派生クラスEliteSaiyan

このとき EliteSaiyan は Saiyan のメンバを継承し、さらに Saiyan は Object のメンバを継承しています。
だから EliteSaiyan も、結果として Object クラスのメンバを受け継いでいることになります。

つまり、どんなクラスでも Objectクラスと無関係ではいられません。
Objectクラスは、継承のいちばん上にある存在です。

だからすべてのクラスはObjectクラスのメンバを持つ

Objectクラスが共通の親であるということは、Java のすべてのクラスが Objectクラスのメンバを継承しているということです。

本文で代表例として挙げられているのは、次の 3 つのメソッドです。

メソッド機能
equals(Object obj)オブジェクトが引数と同じものであるかどうかを調べる
getClass()オブジェクトのクラスを返す
toString()オブジェクトを表す文字列を返す

これらは、特定のクラスだけの特別な機能ではなく、Objectクラスに由来する共通の基本メソッドです。
だから、どんなクラスのオブジェクトでもこうしたメソッドを持っているわけです。

equals は何をするメソッドなのか

equals は、あるオブジェクトと、引数で渡したオブジェクトが同じものかどうかを調べるためのメソッドです。

ドラゴンボールでたとえるなら、

  • 今見ている戦士
  • 比べようとしている戦士

が、同じ存在かどうかを確認する感覚です。

同じ流派かどうか、似た名前かどうかではなく、オブジェクトとして同じかどうかを見る基本機能として覚えるとわかりやすいです。

getClass は何をするメソッドなのか

getClass は、そのオブジェクトがどのクラスに属しているかを返します。

ドラゴンボール風に言えば、

  • この戦士は Saiyan クラスなのか
  • EliteSaiyan クラスなのか
  • それとも別のクラスなのか

を確かめるようなものです。

これは、見た目の変数型ではなく、そのオブジェクト自身の実際のクラス情報を知るためのメソッドとして考えると理解しやすいです。

toString は何をするメソッドなのか

toString は、オブジェクトを文字列で表したものを返すメソッドです。
そして、オブジェクトをそのまま出力しようとしたときに、この toString が呼び出されます。

これはとても便利なしくみです。

なぜなら、クラスごとに toString を上書きしておけば、そのオブジェクトを表示したときに、意味のあるわかりやすい文字列を返せるからです。

たとえばドラゴンボール風に Saiyan クラスを考えるなら、

  • 名前
  • 戦闘力
  • 所属エリア

などをまとめて、ひと目でわかる形で返せるようにできます。

何も準備しないとObjectクラスのtoStringが使われる

自分のクラスで toString を特に用意しなかった場合は、Objectクラスから継承した toString がそのまま使われます。
その結果、Saiyan@数値 のような形式の文字列が表示されます。(※数値は16進数)これは機械的な識別用の表示で、人間にとってはあまりわかりやすくありません。

ドラゴンボールで言えば、戦士を表示したのに、

  • 誰なのか
  • どんな状態なのか

が見えず、識別コードのようなものだけが出てくる感覚です。

それでは少し不便なので、toString は自分のクラスでオーバーライドして、わかりやすい説明文字列を返すようにすると便利です。

Objectクラスを知ると継承の全体像が見える

Objectクラスは、個別のテクニックというより、Java の継承全体を支える土台です。

  • クラスの継承は1つの親だけ
  • でもその親をたどっていくと、最終的にはすべて Object にたどり着く
  • だから Java のすべてのクラスは共通の基本メソッドを持っている

この流れが見えると、継承はばらばらの仕組みではなく、ひとつの大きな系譜だとわかります。

ドラゴンボールの世界でいえば、

  • 戦士ごとに個性は違う
  • 系統も違う
  • でももっと上までたどれば、全員が同じ基本の土台を持つ

という整理です。

この感覚がつかめると、Objectクラスは単なる名前だけの親ではなく、Java のクラス設計全体をまとめる中心だと感じられるようになります。

ドラゴンボールで感覚的に整理する

最後に、ドラゴンボールでひとつのイメージにまとめてみます。

Java のクラスたちは、いろいろな戦士の型です。

  • サイヤ人の型がある
  • その派生としてエリートサイヤ人の型がある
  • 別の戦士系統の型もある

ただし、1人の戦士が同時に複数の親クラスを持つことはできません。
これが多重継承の制限です。親クラスは1本だけです。

その一方で、どんな戦士クラスも上までたどれば、Objectクラスという共通の親に行き着きます。
だから、どの戦士も

  • equals
  • getClass
  • toString

のような基本機能を共通に持っています。

つまり、

  • 継承は1本の系譜でつながる
  • その最上位には Objectクラスがある
  • だから Java のすべてのクラスは、共通の基礎を持ちながら個性を広げている

ということです。

この見方ができると、多重継承の制限も、Objectクラスの意味も、ばらばらの知識ではなく、継承全体のルールとしてすっきり理解できるようになります。

図で多重継承の制限とObjectクラスの関係を整理する

中央上の Objectクラス が、Java の継承階層のいちばん上にあります。
そこから Saiyanクラス や Namekianクラス が分かれ、さらに Saiyanクラス の下に EliteSaiyanクラス がつながっています。

この図から、クラスは縦につながる系譜を持ちながら、最終的には Objectクラスへ集まることがわかります。

右側のバツ印つきの図は、1つのサブクラスが同時に2つのスーパークラスを持つことはできない、という Java のルールを示しています。

そして equals()、getClass()、toString() は、Objectクラスが持つ代表的な基本メソッドです。
すべてのクラスが Object につながるので、こうした共通機能を持てるわけです。