Java入門|getClass()の使い方と役割(クラス情報の取得)

見た目は同じ配列の中でも、戦士の正体まではちゃんと違う。
getClass() を知ると、オブジェクトが本当にどのクラスに属しているのかを、正確につかめるようになる。

ここまで、Java のオブジェクト指向では、スーパークラス型の変数でサブクラスのオブジェクトを扱えることや、オーバーライドによって同じメソッド名でも実体に応じた処理が動くことを見てきました。こうしたしくみはとても便利ですが、その一方で、
「今このオブジェクトは、実際にはどのクラスのものなのか」
を知りたくなる場面も出てきます。

たとえば、配列の型は Saiyan[] なのに、中に入っているのは

  • Saiyan
  • EliteSaiyan

のように別々のクラスかもしれません。
見た目は同じ型でまとめて扱えても、実際の正体までは変数の宣言だけではわからないことがあります。

そんなときに使うのが getClass() メソッドです。
getClass() は、オブジェクトが属しているクラスの情報を返すメソッドです。これは Object クラスに用意されているメソッドなので、Java のすべてのクラスで使えます。

ドラゴンボールでたとえると、これは
「この戦士はサイヤ人として並んでいるけれど、実際には通常のサイヤ人なのか、それともエリートサイヤ人なのか」
を確かめるようなものです。

今回は、そんな getClass() の役割を、ドラゴンボールの世界観で置きかえながら、やさしく整理していきます。特に、

  • getClass() は何を返すのか
  • なぜクラス情報を知ることができるのか
  • スーパークラス型の配列で扱っているときに何が見えるのか
  • Class クラスのオブジェクトとは何か

を順番に見ていきます。

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

getClass() は、そのオブジェクトが属しているクラスの情報を返すメソッド です。
つまり、オブジェクトの正体となるクラスを調べるためのメソッドです。

Java では、変数の型と、実際に中に入っているオブジェクトのクラスが必ずしも同じとは限りません。
とくに継承を使っていると、

  • 変数の型はスーパークラス
  • 実体はサブクラス

という状態がよくあります。

このとき getClass() を使うと、そのオブジェクト自身がどのクラスに属しているのかを知ることができます。

ドラゴンボールでたとえるなら、戦士の整列名簿では全員が「サイヤ人」として並んでいても、実際には

  • 通常のサイヤ人
  • エリートサイヤ人

のどちらなのかを確認するような感覚です。

なぜ getClass() が使えるのか

getClass() は Object クラスのメソッドです。
Java のクラスはすべて最終的に Object クラスを継承しているので、どんなオブジェクトでも getClass() を使えます。

つまり、

  • Saiyan
  • EliteSaiyan
  • ほかの自作クラス

であっても、Object クラスから getClass() を受け継いでいます。

この点は、これまで見てきた toString() や equals() と同じ流れです。
Object クラスが共通の親だからこそ、オブジェクトの基本的な調査メソッドをどのクラスでも使えるわけです。

getClass() が返すのは何か

ここで大事なのは、getClass() が単なる文字列を返しているわけではないことです。
getClass() は、Class クラスのオブジェクト を返します。

つまり、オブジェクトが属しているクラスに関する情報をまとめた、特別なオブジェクトを返しているのです。

この流れを整理すると、こうなります。

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

この Class オブジェクトを使うことで、そのオブジェクトのクラス情報を確認できるわけです。

ドラゴンボール風に言うなら、戦士を見て「この戦士はどのクラスの型に属するのか」という情報カードを取り出すようなものです。

サンプルプログラム:getClass() の動きを確認

ここでは、ドラゴンボール風のクラスで getClass() の動きを確認していきます。
Saiyan 型の配列に通常のサイヤ人とエリートサイヤ人のオブジェクトを入れ、それぞれのクラス情報を調べる流れです。

ファイル名:Sample9.java

class Saiyan
{
    protected String name;
    protected int power;

    public Saiyan()
    {
        name = "戦士なし";
        power = 0;
        System.out.println("サイヤ人を作成しました。");
    }
}

class EliteSaiyan extends Saiyan
{
    private int area;

    public EliteSaiyan()
    {
        area = 0;
        System.out.println("エリートサイヤ人を作成しました。");
    }
}

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

        warriors[0] = new Saiyan();
        warriors[1] = new EliteSaiyan();

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

このプログラムで見ておきたいこと

このプログラムのポイントは、配列の型が Saiyan[] になっているところです。

Saiyan[] warriors;

この宣言を見ると、全部サイヤ人として扱っているように見えます。
でも実際に入れているオブジェクトは 2 種類あります。

配列の位置実際に入っているオブジェクト
warriors[0]Saiyan
warriors[1]EliteSaiyan

つまり、見た目は同じ Saiyan[] の配列でも、中身の実体はそろっていません。
ここで getClass() を使うと、それぞれの正体がわかります。

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

ここが getClass() のとても大事なところです。

warriors の型は Saiyan[] です。
でも getClass() は、変数の宣言型を返しているのではなく、実際にその場所に入っているオブジェクトのクラス を返します。

だから、

  • warriors[0] には Saiyan オブジェクトが入っているので class Saiyan
  • warriors[1] には EliteSaiyan オブジェクトが入っているので class EliteSaiyan

となります。

これは、多態性の考え方とも深くつながっています。
スーパークラス型でまとめて扱っていても、オブジェクト自身はちゃんと自分のクラスを持っている、ということです。

ドラゴンボールで言えば、整列のラベルは全員「サイヤ人」でも、実際にひとりずつ見ていけば、

  • これは通常のサイヤ人
  • これはエリートサイヤ人

と区別できる感覚です。

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

本文では、getClass() の戻り値を Class 型の変数 cl に代入しています。

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

ここで受け取っているのは、そのオブジェクトのクラスに関する情報です。

学習段階では、まずは次のように理解するとわかりやすいです。

  • オブジェクトには「実際にどのクラスのものか」という情報がある
  • getClass() はその情報を取り出す
  • その情報は Class クラスのオブジェクトとして扱われる

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

ドラゴンボールでたとえるなら、戦士を見たときに
「この戦士はどの戦士型に属するか」
が書かれた情報カードを受け取るようなイメージです。

出力結果からわかること

このプログラムでは、最終的に

  • 1番目のオブジェクトは class Saiyan
  • 2番目のオブジェクトは class EliteSaiyan

のように表示されます。

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

これを表で整理すると、次のようになります。

調べる対象getClass() の結果
warriors[0]class Saiyan
warriors[1]class EliteSaiyan

つまり、スーパークラス型で扱っているかどうかに関係なく、そのオブジェクトが本当に属しているクラスを知ることができるわけです。

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

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

たとえば、

  • 配列やコレクションにいろいろな派生クラスが入っているとき
  • どのクラスのオブジェクトが作られているか確認したいとき
  • デバッグ中に、見た目の型と実体が一致しているか確かめたいとき

などに役立ちます。

ドラゴンボールで言えば、戦士の一覧を見ながら
「この札の先にいるのは、通常戦士なのか、エリート戦士なのか」
を確認するようなものです。

getClass() と多態性のつながり

getClass() を学ぶと、多態性の理解も深まります。

多態性では、スーパークラス型の変数でサブクラスのオブジェクトを扱えました。
そのため、見た目の型と実際のオブジェクトのクラスがずれることがあります。

たとえば、

  • 変数の型は Saiyan
  • 実体は EliteSaiyan

という状態です。

このとき show() のようなメソッド呼び出しでは、実体に応じて子クラス側が動きました。
そして getClass() を使えば、その「実体が何か」を文字通り確認できます。

つまり getClass() は、
多態性の中で見えにくくなりがちなオブジェクトの正体を確かめるための道具
とも言えます。

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

getClass() も equals() や toString() と同じく、Object クラスのメソッドです。
だから Java のすべてのクラスで使えます。

この流れを整理すると、こうなります。

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

どれも Object クラスから受け継いでいる基本メソッドです。
だから Object クラスを知っていると、「なぜどのクラスでもこのメソッドが使えるのか」が自然につながって見えてきます。

図で getClass() の流れを整理する

getClass() は、配列の中に入っているオブジェクトと、その結果として返る Class オブジェクトの関係を図で見るとわかりやすいです。

左側では、Saiyan 型の配列 warriors[] に、通常の Saiyan オブジェクトと EliteSaiyan オブジェクトが入っている様子を見ます。

中央から右にかけては、それぞれのオブジェクトに対して getClass() を呼び出した結果、Class クラスのオブジェクトとしてクラス情報が返されていることを表しています。

つまり、同じ配列でまとめて扱っていても、オブジェクトごとの正体は getClass() によってきちんと確認できる、ということです。

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

最後に、ドラゴンボールの感覚で整理してみましょう。

getClass() は、戦士オブジェクトに対して
お前はどの戦士型に属しているのか
を聞くためのメソッドです。

見た目は同じサイヤ人として並んでいても、実際には

  • 通常のサイヤ人
  • エリートサイヤ人

のように違うクラスに属していることがあります。

そのとき getClass() を使えば、その戦士の本当の所属クラスを知ることができます。
しかも返ってくるのは、ただの文字ではなく、クラス情報をまとめた Class クラスのオブジェクトです。

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

この感覚がつかめると、多態性でまとめて扱っているオブジェクトの中身を、よりはっきりイメージできるようになります。