Java入門|instanceofでオブジェクトの型を判定する

見た目は同じ戦士の配列でも、実体までは同じとは限らない。
instanceof を知ると、オブジェクトの正体を見分けながら、安全に処理を分けられるようになる。

抽象クラスを使うと、共通の親クラスの型で、いろいろなサブクラスのオブジェクトをまとめて扱えるようになります。
これはとても便利です。たとえば「戦士」という共通の型で、サイヤ人やナメック星人戦士を同じ配列に入れて扱えるようになります。共通のメソッドを同じ形で呼び出せるので、コードがとても整理しやすくなります。

ただ、その一方で、まとめて扱っているオブジェクトの中から
「これはサイヤ人なのか」
「これはナメック星人戦士なのか」
を調べたくなる場面もあります。

ドラゴンボールでたとえると、全員を Warrior 型の配列に並べていたとしても、特定の処理はサイヤ人にだけ行いたい場合があります。
たとえば、

  • サイヤ人にだけ専用の訓練を行う
  • ナメック星人戦士には別の処理をする
  • 表示内容をクラスごとに少し変えたい

ということがあるわけです。

そんなときに便利なのが instanceof 演算子です。
instanceof を使うと、変数が指しているオブジェクトが、特定のクラスのものかどうかを調べられます。つまり、オブジェクトの型を判定するための道具 です。

今回は、この instanceof の使い方と役割を、ドラゴンボールの戦士たちに置きかえながら、やわらかく丁寧に整理していきます。特に、

  • instanceof は何を調べるのか
  • 抽象クラスの配列でまとめて扱うときに、なぜ便利なのか
  • true と false はどう決まるのか
  • どんな場面で使うと役立つのか

を順番に見ていきます。

instanceof は何をする演算子なのか

instanceof は、左辺の変数が指しているオブジェクトが、右辺のクラスのものかどうかを調べる演算子 です。
結果は true か false で返されます。

たとえば、ドラゴンボール風に書けば次のような形です。

warriors[i] instanceof Saiyan

これは、warriors[i] が指しているオブジェクトが Saiyan クラスのものなら true、そうでなければ false になります。

つまり instanceof は、
このオブジェクトは本当にこのクラスの仲間か
を確認するための演算子です。

なぜ instanceof が必要になるのか

抽象クラスやスーパークラスを使うと、複数の種類のオブジェクトをまとめて扱えるようになります。
これはとても便利ですが、見た目の型が共通になっている分、実際の中身は何なのかが見えにくくなることがあります。

たとえば Warrior[] 型の配列があるとします。
この配列の中には、

  • Saiyan
  • NamekianWarrior

のような異なるクラスのオブジェクトを入れられます。

でも配列の型だけを見ると、全部 Warrior として扱われています。
このとき、

  • これはサイヤ人なのか
  • これはサイヤ人ではないのか

を判定したい場面が出てきます。

そこで instanceof を使うと、まとめて扱っているオブジェクトの中から、特定のクラスだけを判定できます。
つまり instanceof は、多態性でまとめたオブジェクトを、必要に応じて見分けるための演算子 でもあります。

ドラゴンボールで考える instanceof の感覚

ドラゴンボールでたとえると、instanceof は
この戦士は本当にサイヤ人クラスに属しているのか
を確認するようなものです。

たとえば、戦士たちを Warrior[] 型の配列で管理していたとします。
そこには、

  • ベジータのようなサイヤ人
  • ピッコロのようなナメック星人戦士

が混ざっているかもしれません。

このとき、

warriors[i] instanceof Saiyan

と書けば、その戦士がサイヤ人なら true、そうでなければ false です。

つまり instanceof は、
まとめて並んでいる戦士の中から、特定の型を見分けるためのチェック
と考えるとわかりやすいです。

サンプルプログラム:instanceof の動きを確認

ここでは、ドラゴンボール風のクラスで instanceof の動きを確認していきます。
Warrior、Saiyan、NamekianWarrior を用いて整理します。内容は、抽象クラス Warrior 型の配列に 2 つのサブクラスのオブジェクトを入れ、その中からサイヤ人かどうかを instanceof で判定する流れです。

ファイル名:Sample2.java

abstract class Warrior
{
    protected int speed;

    public void setSpeed(int s)
    {
        speed = s;
        System.out.println("速度を" + speed + "にしました。");
    }

    abstract void show();
}

class Saiyan extends Warrior
{
    private String name;
    private int power;

    public Saiyan(String n, int p)
    {
        name = n;
        power = p;
        System.out.println(name + " 戦闘力" + power + "のサイヤ人を作成しました。");
    }

    public void show()
    {
        System.out.println("サイヤ人の名前は" + name + "です。");
        System.out.println("戦闘力は" + power + "です。");
        System.out.println("速度は" + speed + "です。");
    }
}

class NamekianWarrior extends Warrior
{
    private String name;
    private String specialSkill;

    public NamekianWarrior(String n, String sk)
    {
        name = n;
        specialSkill = sk;
        System.out.println(name + " 特技" + specialSkill + "のナメック星人戦士を作成しました。");
    }

    public void show()
    {
        System.out.println("ナメック星人戦士の名前は" + name + "です。");
        System.out.println("特技は" + specialSkill + "です。");
        System.out.println("速度は" + speed + "です。");
    }
}

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

        warriors[0] = new Saiyan("ベジータ", 18000);
        warriors[1] = new NamekianWarrior("ピッコロ", "魔貫光殺砲");

        for(int i = 0; i < warriors.length; i++){
            if(warriors[i] instanceof Saiyan)
                System.out.println((i + 1)
                                   + "番目のオブジェクトはSaiyanクラスです。");
            else
                System.out.println((i + 1)
                                   + "番目のオブジェクトはSaiyanクラスではありません。");
        }
    }
}

実行結果

ベジータ 戦闘力18000のサイヤ人を作成しました。
ピッコロ 特技魔貫光殺砲のナメック星人戦士を作成しました。
1番目のオブジェクトはSaiyanクラスです。
2番目のオブジェクトはSaiyanクラスではありません。

このプログラムで見てほしいところ

このプログラムでは、Warrior[] という配列を用意して、そこに 2 種類のオブジェクトを入れています。

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

見た目の型はどちらも Warrior です。
でも実体は違います。

そこでループの中で、次の判定を行っています。

warriors[i] instanceof Saiyan

この式によって、今見ているオブジェクトが Saiyan クラスかどうかを調べています。

true と false はどう決まるのか

この例では、warriors[0] には Saiyan オブジェクトが入っています。
そのため、

warriors[0] instanceof Saiyan

は true になります。

一方、warriors[1] には NamekianWarrior オブジェクトが入っています。
なので、

warriors[1] instanceof Saiyan

は false になります。

これを表にすると、こうなります。

判定する式結果理由
warriors[0] instanceof Saiyantrue実体が Saiyan だから
warriors[1] instanceof Saiyanfalse実体が NamekianWarrior だから

つまり instanceof は、変数の宣言型ではなく、実際に指しているオブジェクトのクラスを見て判定している わけです。

なぜ変数の型ではなく実体で判定するのか

ここは多態性と深くつながるポイントです。

warriors は Warrior[] 型の配列です。
だから見た目だけを見ると、全部 Warrior に見えます。
でもオブジェクト指向では、変数の型と実体のクラスが同じとは限りません。

このプログラムでも、

  • 型は Warrior
  • 実体は Saiyan や NamekianWarrior

になっています。

instanceof は、その「実体は何か」を見て判定しています。
だからこそ、スーパークラス型でまとめて扱っているオブジェクトの中から、特定のサブクラスだけを見つけることができるのです。

instanceof が便利な場面

instanceof は、特定のクラスのオブジェクトにだけ特別な処理をしたいときに便利です。本文でもそのような使い方が示されています。

たとえばドラゴンボール風に考えると、戦士たちをまとめて扱う中で、

  • サイヤ人にだけ超サイヤ人訓練を行う
  • ナメック星人戦士にだけ再生能力の処理を行う
  • 特定クラスのオブジェクトだけ別のメッセージを出す

といった場合に役立ちます。

つまり instanceof は、
まとめて扱う便利さを保ちながら、必要なときだけ個別判定を入れるための道具
と考えるとわかりやすいです。

抽象クラスと instanceof は相性がよい

今回の例でもわかるように、抽象クラスの配列やスーパークラス型の変数でオブジェクトをまとめて扱う場面では、instanceof が特に便利です。

抽象クラスを使うと、

  • 共通メソッドは同じ形で呼び出せる
  • サブクラスのオブジェクトを1つの配列で管理できる

という良さがあります。

でもその中で、「このクラスのものだけを見分けたい」ということもあります。
そんなとき instanceof があると、共通化と個別判定を両立できます。

ドラゴンボールで言えば、

  • まずは全員を Warrior としてまとめる
  • でも必要な場面だけ、サイヤ人かどうかを見分ける

という使い方です。

instanceof と getClass() の違いをどう考えるか

ここで、前に学んだ getClass() と少し似て見えるかもしれません。
どちらもオブジェクトのクラスに関係するからです。

ただし役割は少し違います。

しくみ役割
instanceof特定のクラスかどうかを true / false で判定する
getClass()そのオブジェクトのクラス情報そのものを取り出す

instanceof は、「サイヤ人かどうか」「ナメック星人かどうか」といった判定に向いています。
一方 getClass() は、「このオブジェクトのクラスは何か」を情報として知りたいときに向いています。

つまり、

  • instanceof は質問に答える道具
  • getClass() は情報を取り出す道具

という違いで見ると整理しやすいです。

図で instanceof のしくみを整理する

instanceof は、配列の中のオブジェクトと判定結果を図で見るとかなりわかりやすくなります。

左側では、Warrior 型の配列 warriors[] に、Saiyan と NamekianWarrior の2つのオブジェクトが入っている様子を見ます。
つまり、見た目は同じ Warrior 型でも、実際の中身は違っています。

中央では、warriors[i] instanceof Saiyan という判定を行っています。
Saiyan オブジェクトなら true、NamekianWarrior オブジェクトなら false になります。

この図から、instanceof は配列や抽象クラスでまとめて扱っているオブジェクトの中から、特定のクラスだけを見分けるための演算子だとわかります。

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

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

instanceof は、戦士オブジェクトに対して
お前はサイヤ人クラスの戦士か
と問いかけるような演算子です。

  • ベジータなら true
  • ピッコロなら false

になります。

大事なのは、全員を Warrior としてまとめて扱っていても、実体のクラスはちゃんと見分けられることです。
だから、抽象クラスで整理されたコードの中でも、必要に応じて特定のクラスだけを判定して処理を分けられます。

つまり instanceof は、
まとめて扱う便利さを保ちながら、オブジェクトの型を見分けるための演算子
です。

この感覚がつかめると、抽象クラス・多態性・型判定のつながりが、とてもきれいに見えてくるようになります。