Java道|Objectクラスと継承のしくみ

どんなクラスも、たどっていけば Object に行き着く。
Object クラスを知ると、Javaの継承がひとつの大きな階層としてつながっていることが見えてきます。

Javaでは、クラスを継承によって広げていけます。

親クラスであるスーパークラスを土台にして、子クラスであるサブクラスを作ります。
さらに、そのサブクラスをもう一段階拡張して、新しいサブクラスを作ることもできます。

このように、クラス同士が親子関係でつながっていく構造を クラスの階層 といいます。

鬼滅の刃風にたとえると、まず鬼殺隊士という共通の土台があります。
そこから柱、隠、継子のように、役割ごとのクラスが分かれていくイメージです。

さらに、柱の中でも水柱、炎柱、風柱のように、もう一段階細かいクラスへ広がることも考えられます。

ただし、Javaの継承には大切なルールがあります。

できること・できないこと内容
1つのスーパークラスから複数のサブクラスを作るできる
サブクラスをさらに拡張するできる
1つのクラスが複数のスーパークラスを同時に継承するできない
スーパークラスを書かないクラスが Object につながる自動的につながる

特に重要なのが、スーパークラスを明示しないクラスは、自動的に Object クラスを親に持つ というルールです。

つまり、Javaで作るすべてのクラスは、最終的には Object クラスにつながっています。
Object クラスは、Javaのあらゆるクラスにとっての共通の親です。

クラスの階層を作るとはどういうことか

Javaでは、1つのスーパークラスをもとにして、複数のサブクラスを作れます。

鬼滅の刃風にたとえると、まず DemonSlayer という鬼殺隊士の共通クラスがあるとします。

そこから、次のようなサブクラスを作れます。

スーパークラスサブクラスの例意味
DemonSlayerPillarSlayer柱の隊士
DemonSlayerKakushiMember隠の隊士
DemonSlayerTsugukoSlayer継子の隊士

このように、1つの親クラスから複数の子クラスへ分かれていくことができます。

さらに、PillarSlayer を土台にして、もう一段階細かいクラスを作ることもできます。

上位クラス下位クラス意味
DemonSlayerPillarSlayer柱の基本クラス
PillarSlayerWaterPillarSlayer水柱
PillarSlayerFlamePillarSlayer炎柱

このように、親から子へ、さらにその子へとつながっていく構造が、クラスの階層です。

鬼滅の刃風にたとえると、鬼殺隊士という大きな土台から、柱という特別な役割が生まれ、その中でさらに水柱や炎柱のような個性が分かれていく流れです。

↓クリックすると拡大表示されます。

サブクラスをさらに拡張すると何が起こるのか

サブクラスをさらに拡張した場合、新しく作ったクラスは、直前の親クラスだけでなく、さらに上のクラスの性質も受け継ぎます。

たとえば、次のような関係を考えます。

class DemonSlayer
{
}

class PillarSlayer extends DemonSlayer
{
}

class WaterPillarSlayer extends PillarSlayer
{
}

この場合、WaterPillarSlayer は PillarSlayer を継承しています。
そして、PillarSlayer は DemonSlayer を継承しています。

つまり、WaterPillarSlayer は次の要素を持つことになります。

受け継ぐもの内容
WaterPillarSlayer 自身の特徴水柱としての独自要素
PillarSlayer の特徴柱としての要素
DemonSlayer の特徴鬼殺隊士としての共通要素
Object の特徴Javaオブジェクトとしての基本機能

継承は、直前の親だけで止まるわけではありません。
階層を上にたどって、親、その親、さらにその親の性質がつながっていきます。

鬼滅の刃風にたとえると、水柱は水柱としての個性を持っています。
同時に、柱でもあり、鬼殺隊士でもあり、Java上では Object につながるオブジェクトでもあります。

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

Javaでは、1つのクラスが複数のスーパークラスを同時に継承することはできません。

たとえば、次のような2つの親クラスがあるとします。

class DemonSlayer
{
}

class MedicalUnit
{
}

この2つを同時に継承して、次のように書くことはできません。

// これはできない
class HealingPillar extends DemonSlayer, MedicalUnit
{
}

Javaのクラス継承では、親クラスは1つだけです。

鬼滅の刃風にたとえると、1人の隊士クラスが「鬼殺隊士の型」と「医療部隊の型」を、両方とも親クラスとして同時に持つような形は作れません。

なぜなら、複数の親クラスを同時に持つと、同じ名前のメソッドやフィールドがあった場合に、どちらを優先するのかが複雑になりやすいからです。

Javaはここをシンプルに保つため、クラスの継承は1本の親だけにしています。

しくみJavaでの扱い
クラスの継承親クラスは1つだけ
複数の親クラスを同時に継承できない
サブクラスをさらに継承できる

それでも共通の能力を増やしたいとき

Javaでは、クラスの継承で複数の親クラスを持つことはできません。

しかし、複数の能力や役割を持たせたい場合には、インターフェイスを使えます。

たとえば、鬼滅の刃風に考えると、隊士には次のような能力があります。

能力インターフェイス風の考え方
呼吸を使えるBreathingUser
任務を指揮できるLeader
仲間を補助できるSupporter

クラスとしての親は1つだけでも、能力の約束は複数組み合わせられます。

class WaterPillarSlayer extends DemonSlayer implements BreathingUser, Leader
{
}

このように考えると、次の違いが見えてきます。

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

今回の中心は Object クラスと継承ですが、Javaが「クラスの親は1つだけ」にしている一方で、「役割や能力はインターフェイスで複数持てる」ようにしていることも、あわせて覚えておくと整理しやすいです。

Objectクラスはすべてのクラスの共通の親

ここからが今回の中心です。

Javaでは、クラスを宣言するときにスーパークラスを指定しない場合、そのクラスは自動的に Object クラスを親に持ちます。

たとえば、次のように書いたとします。

class DemonSlayer
{
}

extends を書いていません。
しかし、考え方としては次のような関係になります。

class DemonSlayer extends Object
{
}

つまり、何も書いていなくても、DemonSlayer は Object クラスの子クラスです。

このルールがあるため、Javaのクラスは最終的にすべて Object クラスにつながります。

書いたクラス実際の継承関係の考え方
class DemonSlayerDemonSlayer extends Object
class PillarSlayer extends DemonSlayerPillarSlayer extends DemonSlayer extends Object
class WaterPillarSlayer extends PillarSlayerWaterPillarSlayer extends PillarSlayer extends DemonSlayer extends Object

Object クラスは、Javaのすべてのクラスにとっての一番上の親です。

鬼滅の刃風に考える Object クラス

鬼滅の刃風にたとえると、Object クラスは「すべての隊士や道具が、Javaの世界で共通に持っている最も基本の型」です。

鬼殺隊士、柱、隠、刀、任務書など、いろいろなクラスを作れるとします。

それぞれ見た目も役割も違います。

クラス鬼滅の刃風の役割
DemonSlayer鬼殺隊士
PillarSlayer
NichirinSword日輪刀
MissionScroll任務書

しかし、Javaのクラスとしては、どれも最終的に Object につながります。

つまり Object クラスは、鬼滅の刃風に言えば、
隊士や道具などの種類が分かれる前にある、Javaオブジェクトとしての共通土台
のような存在です。

Objectクラスにつながる階層の見方

Object クラスを一番上に置いて、クラスの階層を整理すると分かりやすくなります。

階層
最上位の共通の親Object
その子クラスDemonSlayer
さらにその子クラスPillarSlayer
さらにその子クラスWaterPillarSlayer

このような階層になっている場合、WaterPillarSlayer は WaterPillarSlayer 自身の特徴だけを持っているわけではありません。

次のように、上位クラスの性質も積み重ねています。

クラス受け継ぐもの
ObjectJavaオブジェクトとしての基本機能
DemonSlayer鬼殺隊士としての共通機能
PillarSlayer柱としての共通機能
WaterPillarSlayer水柱としての独自機能

このように見ると、Javaの継承はバラバラの親子関係ではなく、Object を頂点にした大きな階層としてつながっていることが分かります。

図:Objectクラスを頂点にした継承階層

↓クリックすると拡大表示されます。

この図が示していること

この図では、Object クラスが一番上にあり、その下に DemonSlayer や NichirinSword のようなクラスがつながっています。

DemonSlayer の下には PillarSlayer、さらにその下に WaterPillarSlayer が続いています。

この形から、下のクラスほど上のクラスの性質を積み重ねて受け継いでいることが分かります。

また、右下の注意カードでは、1つのクラスが複数のスーパークラスを同時に継承できないことも示しています。

つまりこの図は、次の2つを表しています。

ポイント内容
Object はすべてのクラスの共通の親何も extends しないクラスも Object につながる
Javaのクラス継承は単一継承1つのクラスが持てる直接の親クラスは1つだけ

Javaのすべてのクラスは Object のメンバを持つ

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

これはとても大事です。

なぜなら、Object クラスが持っている基本メソッドを、どんなクラスのオブジェクトでも使えるからです。

代表的なメソッドには、次のようなものがあります。

メソッド役割
equals(Object obj)引数のオブジェクトと同じかどうかを調べる
getClass()オブジェクトのクラス情報を返す
toString()オブジェクトを表す文字列を返す

これらは、特定のクラスだけのものではありません。
Javaのあらゆるクラスが Object につながっているため、共通して使える基本メソッドです。

equals の役割

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

鬼滅の刃風にたとえると、
この隊士と、比較している隊士は同じ存在なのか
を確認するようなイメージです。

slayer1.equals(slayer2);

このように使うと、slayer1 と slayer2 を比較できます。

ただし、equals の動きはクラスによってオーバーライドされることがあります。

何もオーバーライドしていない場合は、基本的には同じオブジェクトを指しているかどうかを見る考え方になります。
一方で、クラス側で equals を作り直せば、名前やIDなどをもとに「同じ隊士かどうか」を判断するようにもできます。

使い方考え方
Object 由来の equals同じオブジェクトかどうかを見る
オーバーライドした equalsクラスに合った同一性の判断をする

鬼滅の刃風にたとえると、ただ同じ場所にいるかではなく、隊士番号や名前などをもとに同一人物か判断するように作り直せる、ということです。

getClass の役割

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

鬼滅の刃風にたとえると、
このオブジェクトは DemonSlayer なのか、PillarSlayer なのか、WaterPillarSlayer なのか
を確認するようなものです。

slayer1.getClass();

このメソッドを使うと、変数の型ではなく、実際のオブジェクトのクラス情報を確認できます。

これは、ポリモーフィズムを学んだあとだと理解しやすいです。

たとえば、次のようなコードを考えます。

DemonSlayer slayer1 = new PillarSlayer();

この場合、変数の型は DemonSlayer です。
しかし、実体は PillarSlayer です。

getClass() を使うと、実際のオブジェクトのクラス情報を確認できます。

見るもの内容
変数の型DemonSlayer
実際のオブジェクトPillarSlayer
getClass() で分かるもの実体のクラス情報

鬼滅の刃風にたとえると、名簿上は鬼殺隊士として扱っていても、実際には柱であることを確認できるような感覚です。

toString の役割

Object クラスのメソッドの中でも、特に大切なのが toString です。

toString は、オブジェクトを文字列として表したものを返すメソッドです。

たとえば、オブジェクトを System.out.println で表示するとき、内部では toString の結果が使われます。

System.out.println(slayer1);

このように書いた場合、slayer1 の toString() が呼ばれ、その戻り値が表示されます。

つまり、toString は、
そのオブジェクトを文字列でどう表すか
を決めるメソッドです。

鬼滅の刃風にたとえると、隊士が自分の情報を文字で名乗るための基本メソッドです。

Objectから受け継いだ toString をそのまま使うとどうなるか

何も準備しないままオブジェクトを表示すると、Object クラスから受け継いだ toString がそのまま使われます。

その場合、次のような形式の文字列が表示されることがあります。

DemonSlayer@1a2b3c

これは Java としては正しい表示です。

ただし、人間にとってはあまり分かりやすくありません。

表示分かること
DemonSlayer@1a2b3cクラス名と識別用の情報らしきもの
名前: 水月, 階級: 水柱オブジェクトの意味ある情報

鬼滅の刃風にたとえると、隊士を紹介してほしいのに、名前や階級ではなく、管理番号のようなものだけが表示される感覚です。

確認や学習には少し不便です。

toString をオーバーライドすると便利になる

そこで便利なのが、toString のオーバーライドです。

自分のクラスで toString を作り直すと、そのオブジェクトを表示したときに、意味のある文字列を返せるようになります。

たとえば、DemonSlayer クラスなら、次のようにできます。

class DemonSlayer
{
    private String name;
    private String rank;

    public DemonSlayer(String name, String rank)
    {
        this.name = name;
        this.rank = rank;
    }

    public String toString()
    {
        return "名前: " + name + "、階級: " + rank;
    }
}

このように toString をオーバーライドしておくと、次のように表示できます。

DemonSlayer slayer1 = new DemonSlayer("水月", "水柱");
System.out.println(slayer1);

表示イメージは次のようになります。

名前: 水月、階級: 水柱

これは、Object から受け継いだ toString を、DemonSlayer クラスに合うように作り直している状態です。

鬼滅の刃風にたとえると、ただの管理番号ではなく、隊士が自分の名前や階級をきちんと名乗れるようにするイメージです。

toString はなぜ便利なのか

toString をオーバーライドすると、デバッグや表示がとても分かりやすくなります。

たとえば、オブジェクトの中身を確認したいときに、意味のある情報が表示されるからです。

toString をそのまま使う場合toString をオーバーライドした場合
DemonSlayer@1a2b3c のような表示名前や階級など意味のある表示
中身が分かりにくい状態を確認しやすい
学習中に混乱しやすいオブジェクトの内容をつかみやすい

toString は、Javaのクラスでとてもよく使うオーバーライド対象のひとつです。

図:Object の代表メソッドと toString のオーバーライド

↓クリックすると拡大表示されます。

この図が示していること

この図では、Object クラスが持つ代表的なメソッドと、toString をオーバーライドする意味を整理しています。

Object クラスには、equals、getClass、toString のような基本メソッドがあります。
すべてのクラスは Object につながっているため、こうしたメソッドを共通に持ちます。

右側では、DemonSlayer クラスが toString を自分用に作り直しています。

その結果、何も準備していない場合は DemonSlayer@1a2b3c のような表示になりやすいところを、名前や階級が分かる表示にできます。

この図から分かることは、Object のメソッドはただ受け継ぐだけでなく、必要に応じて自分のクラスらしくオーバーライドできるということです。

なぜ Object クラスを知ることが大切なのか

Object クラスは、普段のコードで明示的に extends Object と書くことが少ないため、最初は少し地味に感じるかもしれません。

しかし、Javaの継承を理解するうえではとても重要です。

理由は、次のとおりです。

理由内容
すべてのクラスが Object につながるJavaの継承階層の根になる
基本メソッドを共通で持つequals、getClass、toString などが使える
オーバーライドの理解につながるtoString などを自分用に作り直せる
ポリモーフィズムの土台になるObject 型でさまざまなオブジェクトを扱える考え方につながる

Object クラスを知ると、Javaのクラスが完全にバラバラに存在しているのではなく、1つの大きな階層としてつながっていることが分かります。

Object 型でいろいろなオブジェクトを扱える

Object はすべてのクラスの親なので、Object 型の変数には、いろいろなクラスのオブジェクトを入れられます。

Object obj1 = new DemonSlayer("水月", "水柱");
Object obj2 = new MissionScroll();
Object obj3 = new NichirinSword();

これは、DemonSlayer も MissionScroll も NichirinSword も、すべて Object の一種だからです。

鬼滅の刃風にたとえると、隊士、任務書、日輪刀のように種類が違っても、Javaの世界ではすべて「オブジェクト」として扱えるということです。

ただし、Object 型として扱う場合、呼び出せるのは基本的に Object にあるメソッドです。
そのクラス独自のメソッドをそのまま呼びたい場合は、型を意識する必要があります。

この考え方は、スーパークラス型でサブクラスを扱うポリモーフィズムともつながります。

鬼滅の刃風に Object クラスを整理する

Javaのクラスは、見た目にはそれぞれ別の存在です。

鬼滅の刃風にたとえると、次のようにさまざまなクラスがあります。

クラス役割
DemonSlayer鬼殺隊士
PillarSlayer
WaterPillarSlayer水柱
NichirinSword日輪刀
MissionScroll任務書

しかし、どれも最終的には Object クラスにつながっています。

そのため、どのクラスも Object 由来の基本メソッドを持っています。

Object のメソッド鬼滅の刃風のイメージ
equals同じ隊士や道具か確認する
getClass何のクラスの実体か調べる
toString自分の情報を文字で名乗る

Object クラスは、
すべてのクラスが最初に受け継いでいる共通の親
です。

この考え方が分かると、Javaの継承は、単なる親子関係の集まりではなく、Object を頂点にしたひとつの大きな仕組みとして見えてきます。

この内容で押さえておきたいポイント

ポイント内容
クラスの階層親から子へ、さらにその子へとつながる構造
サブクラスの拡張上位クラスのメンバも積み重ねて受け継ぐ
Javaの単一継承1つのクラスが直接継承できる親クラスは1つだけ
Object クラスすべてのクラスの共通の親
extends を書かないクラス自動的に Object を親に持つ
equalsオブジェクト同士を比較する基本メソッド
getClass実際のクラス情報を確認するメソッド
toStringオブジェクトを文字列で表すメソッド
toString のオーバーライドオブジェクトを分かりやすく表示できる

Object クラスを理解すると、Javaのクラスがすべて共通の土台につながっていることが分かります。

そして、equals、getClass、toString のような基本メソッドがどのクラスでも使える理由も見えてきます。

特に toString は、自分のクラスらしくオーバーライドすることで、オブジェクトの状態を分かりやすく表示できる便利なメソッドです。