Java道|getClass()の使い方と役割

見た目は同じ型で並んでいても、実体のクラスは一人ひとり違う。
getClass() を理解すると、オブジェクトが本当にどのクラスから作られたのかを正確につかめます。

Javaのオブジェクト指向では、スーパークラス型の変数でサブクラスのオブジェクトを扱えます。

たとえば、DemonSlayer 型の変数や配列に、DemonSlayer オブジェクトだけでなく、PillarSlayer オブジェクトを入れることもできます。

DemonSlayer slayer1 = new PillarSlayer();

このように書くと、変数の型は DemonSlayer ですが、実際に入っているオブジェクトは PillarSlayer です。

ここで気になるのが、
今このオブジェクトは、実際にはどのクラスから作られたものなのか
という点です。

この「実体のクラス情報」を調べるために使うのが getClass() です。

getClass() は、Object クラスに用意されているメソッドです。
Javaのすべてのクラスは最終的に Object クラスを継承しているため、DemonSlayer や PillarSlayer のような自作クラスでも getClass() を使えます。

鬼滅の刃風にたとえると、getClass() は、隊士名簿では「鬼殺隊士」としてまとめられていても、実際には一般隊士なのか、柱なのか、水柱なのかを確認するための身分確認のようなものです。

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

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

つまり、オブジェクトの正体となるクラスを調べるために使います。

基本的な形は次のようになります。

object.getClass()

このように呼び出すと、object が実際にどのクラスから作られたオブジェクトなのかを表す情報が返ってきます。

たとえば、次のような状態を考えます。

DemonSlayer slayer1 = new PillarSlayer();

この場合、変数の型は DemonSlayer です。
しかし、実際に作られているオブジェクトは PillarSlayer です。

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

このとき slayer1.getClass() を呼び出すと、変数の型ではなく、実際のオブジェクトである PillarSlayer のクラス情報が分かります。

鬼滅の刃風にたとえると、名簿では鬼殺隊士として並んでいても、本人確認をすると「この隊士は柱です」と分かるようなイメージです。

なぜ getClass() が使えるのか

getClass() は Object クラスのメソッドです。

Javaのクラスは、すべて最終的に Object クラスにつながっています。
そのため、どんなクラスのオブジェクトでも getClass() を使えます。

これは、toString() や equals() と同じ考え方です。

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

鬼滅の刃風にたとえると、鬼殺隊士、柱、日輪刀、任務書など、いろいろな種類のオブジェクトがあっても、Java世界の共通機能として「自分がどのクラスなのかを示す機能」を持っているということです。

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

getClass() が返すのは何か

getClass() が返すのは、単なる文字列ではありません。

getClass() は、Class クラスのオブジェクト を返します。

Class cl = object.getClass();

この Class オブジェクトには、そのオブジェクトのクラス情報が入っています。

メソッド戻り値
getClass()Class クラスのオブジェクト

ここは少し難しく感じるかもしれませんが、最初は次のように考えると分かりやすいです。

考え方内容
オブジェクト実際に作られたもの
getClass()そのオブジェクトの正体を調べる
Class オブジェクトクラス情報が入った情報カード

鬼滅の刃風にたとえると、隊士本人を見て「この人は PillarSlayer クラスです」と書かれた身分証カードを取り出すようなイメージです。

オブジェクトのクラス情報を調べる

ファイル名:Sample9.java

class DemonSlayer
{
    protected String name;
    protected String rank;

    public DemonSlayer()
    {
        name = "隊士未登録";
        rank = "階級未設定";
        System.out.println("鬼殺隊士を作成しました。");
    }
}

class PillarSlayer extends DemonSlayer
{
    private int area;

    public PillarSlayer()
    {
        area = 0;
        System.out.println("柱クラスの隊士を作成しました。");
    }
}

class Sample9
{
    public static void main(String[] args)
    {
        DemonSlayer[] slayers;
        slayers = new DemonSlayer[2];

        slayers[0] = new DemonSlayer();
        slayers[1] = new PillarSlayer();

        for(int i = 0; i < slayers.length; i++){
            Class cl = slayers[i].getClass();
            System.out.println((i + 1) + "番目のオブジェクトのクラスは"
                               + cl + "です。");
        }
    }
}

実行結果

鬼殺隊士を作成しました。
鬼殺隊士を作成しました。
柱クラスの隊士を作成しました。
1番目のオブジェクトのクラスはclass DemonSlayerです。
2番目のオブジェクトのクラスはclass PillarSlayerです。

Sample9.java の中心になる部分

このプログラムで大切なのは、配列の型が DemonSlayer[] になっているところです。

DemonSlayer[] slayers;
slayers = new DemonSlayer[2];

この配列は DemonSlayer 型の配列です。
そのため、DemonSlayer のオブジェクトを入れられます。

slayers[0] = new DemonSlayer();

さらに、PillarSlayer は DemonSlayer を継承しているため、PillarSlayer オブジェクトも入れられます。

slayers[1] = new PillarSlayer();

整理すると、次のようになります。

配列の位置変数としての型実際に入っているオブジェクト
slayers[0]DemonSlayerDemonSlayer
slayers[1]DemonSlayerPillarSlayer

見た目はどちらも DemonSlayer 型の配列要素です。
しかし、実体は異なります。

ここで getClass() を使うと、それぞれのオブジェクトの正体を確認できます。

なぜ配列の型ではなく実体のクラスが分かるのか

getClass() は、変数の宣言型を返しているのではありません。

getClass() が見るのは、実際にその変数や配列要素が指しているオブジェクトのクラスです。

そのため、次のような結果になります。

調べる対象配列としての型実体getClass() の結果
slayers[0]DemonSlayerDemonSlayerclass DemonSlayer
slayers[1]DemonSlayerPillarSlayerclass PillarSlayer

ここが getClass() の重要なところです。

DemonSlayer[] 配列に入っているからといって、すべてが DemonSlayer の実体だとは限りません。
サブクラスの PillarSlayer オブジェクトが入っている場合もあります。

鬼滅の刃風にたとえると、隊士名簿では全員を鬼殺隊士として並べていても、一人ずつ確認すると「一般隊士」と「柱」が混ざっていることが分かる、ということです。

Class クラスのオブジェクトとは何か

Sample9.java では、getClass() の戻り値を Class 型の変数 cl に入れています。

Class cl = slayers[i].getClass();

この cl には、オブジェクトのクラス情報が入っています。

学習段階では、Class クラスのオブジェクトを、次のようにイメージすると分かりやすいです。

もの鬼滅の刃風のイメージ
DemonSlayer オブジェクト実際の隊士本人
getClass()隊士の身分証を確認する動作
Class オブジェクトどのクラスの隊士かを示す身分証カード

つまり Class オブジェクトは、クラスそのものについての情報を持っている特別な情報箱です。

たとえば、getClass() の結果を出力すると、次のような表示になります。

class DemonSlayer
class PillarSlayer

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

出力結果から分かること

Sample9.java の出力では、次のように表示されています。

1番目のオブジェクトのクラスはclass DemonSlayerです。
2番目のオブジェクトのクラスはclass PillarSlayerです。

ここから分かるのは、getClass() がきちんとオブジェクトの実体を見ているということです。

順番配列要素実体getClass() の結果
1番目slayers[0]DemonSlayerclass DemonSlayer
2番目slayers[1]PillarSlayerclass PillarSlayer

このように、スーパークラス型でまとめて扱っていても、getClass() を使えば、実際のオブジェクトのクラスを確認できます。

getClass() はどんな場面で便利なのか

getClass() は、オブジェクトの種類を確認したいときに便利です。

たとえば、次のような場面があります。

場面getClass() が役立つ理由
配列に複数種類のオブジェクトが入っているどのクラスの実体か確認できる
コレクションに派生クラスが混ざっている中身の正体を調べられる
デバッグ中に型を確認したい変数の型と実体の違いを確認できる
ポリモーフィズムの動きを学びたい実際にどのクラスのオブジェクトか分かる

鬼滅の刃風にたとえると、隊士名簿を見ながら、
この札の先にいるのは、一般隊士なのか、柱なのか
を確認するようなものです。

getClass() とポリモーフィズムのつながり

getClass() は、ポリモーフィズムの理解とも深くつながります。

ポリモーフィズムでは、スーパークラス型の変数でサブクラスのオブジェクトを扱えます。

たとえば、次のような状態です。

DemonSlayer slayer1 = new PillarSlayer();

この場合、

見るポイント内容
変数の型DemonSlayer
実体PillarSlayer

になります。

この状態で show() のようなオーバーライドされたメソッドを呼び出すと、実体である PillarSlayer 側のメソッドが動きます。

そして getClass() を使えば、その「実体が何か」を確認できます。

メソッド見えるもの
オーバーライドされたメソッド呼び出し実体に応じた処理が動く
getClass()実体のクラス情報が分かる

つまり getClass() は、
ポリモーフィズムで見えにくくなりがちなオブジェクトの正体を確認するためのメソッド
と考えると分かりやすいです。

Object クラスとのつながりを意識する

getClass() は、toString() や equals() と同じく Object クラスのメソッドです。

そのため、Javaのすべてのクラスで使えます。

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

Object クラスを理解していると、なぜこれらのメソッドがどのクラスでも使えるのかが自然につながります。

鬼滅の刃風にたとえると、どの隊士や道具も、Java世界の共通の土台である Object から基本機能を受け継いでいるということです。

図:getClass() が実体のクラスを調べる流れ

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

この図が示していること

この図では、DemonSlayer[] 配列の中に、DemonSlayer オブジェクトと PillarSlayer オブジェクトが入っている様子を表しています。

配列の型は DemonSlayer[] です。
しかし、slayers[0] の実体は DemonSlayer、slayers[1] の実体は PillarSlayer です。

getClass() を呼び出すと、配列の型ではなく、実際に入っているオブジェクトのクラス情報が返されます。

呼び出し結果
slayers[0].getClass()class DemonSlayer
slayers[1].getClass()class PillarSlayer

この図から分かることは、getClass() は「見た目の型」ではなく「実体のクラス」を調べるメソッドだということです。

図:Object クラスから受け継ぐ getClass()

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

この図が示していること

この図では、getClass() が Object クラスにある基本メソッドであり、DemonSlayer や PillarSlayer に受け継がれていることを表しています。

Object クラスには、toString()、equals()、getClass() のような基本メソッドがあります。
DemonSlayer は Object を継承し、PillarSlayer は DemonSlayer を継承しています。

そのため、PillarSlayer も最終的には Object 由来の getClass() を使えます。

この図から分かることは、getClass() が特定のクラスだけの機能ではなく、Javaのすべてのオブジェクトが使える共通機能だということです。

鬼滅の刃風に getClass() を整理する

getClass() は、鬼滅の刃風にたとえると、隊士オブジェクトに対して、
あなたはどの隊士型に属していますか
と確認するメソッドです。

見た目は同じ DemonSlayer[] 配列に入っていても、実際には次のように違うクラスのオブジェクトが入っている場合があります。

配列上の見た目実体
DemonSlayer 型として扱うDemonSlayer オブジェクト
DemonSlayer 型として扱うPillarSlayer オブジェクト

このとき getClass() を使えば、それぞれの実体を確認できます。

実体getClass() の結果
DemonSlayerclass DemonSlayer
PillarSlayerclass PillarSlayer

つまり getClass() は、
オブジェクトの正体となるクラスを確認するためのメソッド
です。

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

ポイント内容
getClass()オブジェクトが属しているクラス情報を返すメソッド
定義元Object クラス
すべてのクラスで使える理由Javaのすべてのクラスは Object を継承しているから
戻り値Class クラスのオブジェクト
変数の型との違いgetClass() は変数の宣言型ではなく、実体のクラスを見る
配列での使い方スーパークラス型配列に入った各オブジェクトの正体を確認できる
ポリモーフィズムとの関係スーパークラス型でまとめた中身の実体を確認できる
便利な場面デバッグ、学習、配列やコレクション内の型確認

getClass() を理解すると、スーパークラス型でまとめて扱っているオブジェクトの中身が、実際にはどのクラスなのかを確認できるようになります。

見た目の型と実体のクラスを分けて考える。
この感覚がつかめると、ポリモーフィズムや Object クラスの理解もさらに深まります。