Java超|instanceofの使い方と型判定のしくみ

見た目は同じ戦士名簿の中でも、実体の型までは同じとは限りません。instanceof を理解すると、オブジェクトの正体を見分けながら、安全に処理を分けられるようになります。

抽象クラスを使うと、共通の親クラスの型で、いろいろなサブクラスのオブジェクトをまとめて扱えるようになります。

たとえば、ドラゴンボール風に考えると、Warrior という共通の戦士型を用意して、その配列の中に SuperSaiyanWarrior や SupportWarrior のような具体的な戦士オブジェクトを入れられます。

これはとても便利です。

共通の型でまとめられるので、同じ配列で管理できます。
さらに、共通メソッドも同じ形で呼び出せます。

たとえば、Warrior[] warriors という配列に、スーパーサイヤ人戦士と支援戦士を一緒に入れられます。

Warrior[] warriors = new Warrior[2];

warriors[0] = new SuperSaiyanWarrior("悟空", "亀仙流", 1);
warriors[1] = new SupportWarrior("悟飯", "神流", "気の感知と仲間支援");

配列の型は Warrior[] です。

しかし、実際に入っているオブジェクトは、SuperSaiyanWarrior と SupportWarrior です。

ここで大切になるのは、見た目の型と実体の型が違う場合があるという点です。

確認したいこと
この戦士はスーパーサイヤ人戦士なのかSuperSaiyanWarrior かどうか
この戦士は支援戦士なのかSupportWarrior かどうか
特定のクラスだけに処理したいSuperSaiyanWarrior だけに変身訓練を行う

このようなときに使うのが instanceof 演算子です。

instanceof を使うと、変数が指しているオブジェクトが、特定のクラスのものかどうかを true または false で判定できます。

つまり instanceof は、オブジェクトの型を判定するための道具です。

ドラゴンボール風にたとえると、instanceof は、戦士名簿に並んでいる戦士に対して、

あなたは SuperSaiyanWarrior クラスの戦士ですか

と確認するようなものです。

名簿上は全員 Warrior として並んでいても、実体はそれぞれ違います。

instanceof を使えば、その中から特定の戦士型だけを見分けられます。

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

instanceof は、左側の変数が指しているオブジェクトが、右側のクラスのものかどうかを調べる演算子です。

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

object instanceof ClassName

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

warriors[i] instanceof SuperSaiyanWarrior

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

意味
warriors[i] instanceof SuperSaiyanWarriorwarriors[i] の実体が SuperSaiyanWarrior かどうか調べる
warriors[i] instanceof SupportWarriorwarriors[i] の実体が SupportWarrior かどうか調べる

ここで大切なのは、instanceof は変数の宣言型だけを見るのではなく、実際に指しているオブジェクトの型を見て判定するという点です。

たとえば、warriors という配列の型が Warrior[] だったとしても、中に入っている実体は SuperSaiyanWarrior かもしれません。

その場合、次の判定は true になります。

warriors[i] instanceof SuperSaiyanWarrior

ドラゴンボール風にたとえると、戦士名簿上は Warrior として並んでいても、本人確認をすれば、スーパーサイヤ人戦士なのか、支援戦士なのかを判定できるということです。

なぜ instanceof が必要になるのか

抽象クラスやスーパークラスを使うと、複数の種類のオブジェクトをまとめて扱えます。

たとえば、次のような配列を考えます。

Warrior[] warriors;

この配列には、Warrior を継承したサブクラスのオブジェクトを入れられます。

配列に入れる実体説明
SuperSaiyanWarriorスーパーサイヤ人戦士
SupportWarrior支援戦士

配列の型だけを見ると、どちらも Warrior として扱われます。

しかし、実際の中身は SuperSaiyanWarrior だったり、SupportWarrior だったりします。

このとき、特定のサブクラスだけを見分けたいことがあります。

たとえば、SuperSaiyanWarrior だけに変身段階の確認を行いたい場合です。

また、SupportWarrior だけに支援能力の確認をさせたい場合もあります。

こうしたときに instanceof を使うと、まとめて扱っているオブジェクトの中から、特定の型だけを判定できます。

つまり instanceof は、多態性でまとめたオブジェクトを、必要に応じて見分けるための演算子です。

ドラゴンボール風に言うと、戦士管理本部では全員を Warrior としてまとめて管理します。

しかし、訓練内容を分けたいときには、

スーパーサイヤ人戦士か。
支援戦士か。

を見分ける必要があります。

その判定に使うのが instanceof です。

図:instanceof で特定の型を判定する流れ

この図が示していること

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

配列の型は Warrior[] なので、見た目はどちらも Warrior として扱われています。

しかし、実体は SuperSaiyanWarrior と SupportWarrior で異なります。

中央では、次の判定を行っています。

warriors[i] instanceof SuperSaiyanWarrior
対象実体判定結果
warriors[0]SuperSaiyanWarriortrue
warriors[1]SupportWarriorfalse

この図から分かることは、instanceof は変数の見た目の型ではなく、実際に指しているオブジェクトの型を見て判定するということです。

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

ドラゴンボール風にたとえると、instanceof は、戦士オブジェクトに対して、

あなたは本当にこの型の戦士ですか

と確認する判定札のようなものです。

たとえば、Warrior[] 配列に次のような戦士が入っているとします。

配列要素実体
warriors[0]SuperSaiyanWarrior
warriors[1]SupportWarrior

このとき、次の判定を行います。

warriors[i] instanceof SuperSaiyanWarrior

すると、SuperSaiyanWarrior の実体なら true です。

SupportWarrior の実体なら false です。

実体warriors[i] instanceof SuperSaiyanWarrior
SuperSaiyanWarriortrue
SupportWarriorfalse

これは、全員を Warrior としてまとめて扱いながら、必要なときだけ特定の戦士型を見分けるためのしくみです。

たとえば、スーパーサイヤ人戦士だけに変身訓練を行いたい場合、instanceof で SuperSaiyanWarrior かどうかを判定できます。

支援戦士には支援訓練、スーパーサイヤ人戦士には変身訓練、というように処理を分けられるわけです。

instanceof の動きを確認する

ファイル名:Sample2.java

abstract class Warrior
{
    protected int speed;

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

    abstract void show();
}

class SuperSaiyanWarrior extends Warrior
{
    private String name;
    private String styleName;
    private int transformationLevel;

    public SuperSaiyanWarrior(String n, String s, int level)
    {
        name = n;
        styleName = s;
        transformationLevel = level;
        System.out.println(name + " 変身段階" + transformationLevel + "のスーパーサイヤ人戦士を作成しました。");
    }

    public void show()
    {
        System.out.println("スーパーサイヤ人戦士の名前は" + name + "です。");
        System.out.println("流派は" + styleName + "です。");
        System.out.println("変身段階は" + transformationLevel + "です。");
        System.out.println("速度は" + speed + "です。");
    }
}

class SupportWarrior extends Warrior
{
    private String name;
    private String styleName;
    private String supportSkill;

    public SupportWarrior(String n, String s, String skill)
    {
        name = n;
        styleName = s;
        supportSkill = skill;
        System.out.println(name + " 支援能力「" + supportSkill + "」の支援戦士を作成しました。");
    }

    public void show()
    {
        System.out.println("支援戦士の名前は" + name + "です。");
        System.out.println("流派は" + styleName + "です。");
        System.out.println("支援能力は" + supportSkill + "です。");
        System.out.println("速度は" + speed + "です。");
    }
}

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

        warriors[0] = new SuperSaiyanWarrior("悟空", "亀仙流", 1);
        warriors[1] = new SupportWarrior("悟飯", "神流", "気の感知と仲間支援");

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

実行結果

悟空 変身段階1のスーパーサイヤ人戦士を作成しました。
悟飯 支援能力「気の感知と仲間支援」の支援戦士を作成しました。
1番目のオブジェクトはSuperSaiyanWarriorクラスです。
2番目のオブジェクトはSuperSaiyanWarriorクラスではありません。

Sample2.java の中心になる部分

このプログラムでは、まず抽象クラス Warrior を用意しています。

abstract class Warrior

Warrior は抽象クラスなので、直接 new Warrior() のようにオブジェクトを作ることはできません。

しかし、Warrior 型の配列を作ることはできます。

Warrior[] warriors;
warriors = new Warrior[2];

そして、その配列には Warrior を継承した具体的なサブクラスのオブジェクトを入れられます。

warriors[0] = new SuperSaiyanWarrior("悟空", "亀仙流", 1);
warriors[1] = new SupportWarrior("悟飯", "神流", "気の感知と仲間支援");

配列の中身を整理すると、次のようになります。

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

見た目の型はどちらも Warrior です。

しかし、実体は SuperSaiyanWarrior と SupportWarrior で異なります。

そこで、ループの中で instanceof を使って、SuperSaiyanWarrior かどうかを判定しています。

warriors[i] instanceof SuperSaiyanWarrior

この式が true なら SuperSaiyanWarrior、false なら SuperSaiyanWarrior ではない、という判断ができます。

true と false はどう決まるのか

このプログラムでは、warriors[0] には SuperSaiyanWarrior オブジェクトが入っています。

そのため、次の判定は true になります。

warriors[0] instanceof SuperSaiyanWarrior

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

そのため、次の判定は false になります。

warriors[1] instanceof SuperSaiyanWarrior

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

判定する式結果理由
warriors[0] instanceof SuperSaiyanWarriortrue実体が SuperSaiyanWarrior だから
warriors[1] instanceof SuperSaiyanWarriorfalse実体が SupportWarrior だから

ここで大切なのは、warriors[0] も warriors[1] も、配列上は Warrior 型として扱われていることです。

しかし instanceof は、変数の宣言型ではなく、実体のクラスを見て判定します。

ドラゴンボール風にたとえると、戦士名簿には全員 Warrior として並んでいても、実際にスカウターで確認すると、スーパーサイヤ人戦士なのか支援戦士なのかを見分けられるということです。

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

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

多態性では、スーパークラス型や抽象クラス型の変数で、サブクラスのオブジェクトを扱えます。

たとえば、今回の配列では次のようになっています。

配列要素見た目の型実体
warriors[0]WarriorSuperSaiyanWarrior
warriors[1]WarriorSupportWarrior

このように、見た目の型と実体のクラスが違うことがあります。

instanceof は、この実体を見て判定します。

だから、Warrior 型の配列でまとめていても、その中から SuperSaiyanWarrior だけを見つけることができます。

これは、多態性でまとめて扱う便利さを保ちながら、必要なときだけ個別の型を確認できるということです。

instanceof が便利な場面

instanceof は、特定のクラスのオブジェクトにだけ処理をしたいときに便利です。

たとえば、ドラゴンボール風に考えると、次のような場面です。

場面instanceof の使い方
スーパーサイヤ人戦士だけに変身訓練を行うSuperSaiyanWarrior かどうか判定する
支援戦士だけに支援準備をさせるSupportWarrior かどうか判定する
特定クラスだけ別メッセージを出すif 文で処理を分ける
配列の中身の型を確認するループ中に instanceof で調べる

つまり instanceof は、まとめて扱う便利さを保ちながら、必要なときだけ型によって処理を分けるための道具です。

たとえば、Warrior[] 配列で全員を管理しているとします。

通常は show() を呼び出せば、実体ごとの show() が動きます。

しかし、SuperSaiyanWarrior だけに専用メッセージを出したい場合は、instanceof で判定してから処理を分けられます。

if(warriors[i] instanceof SuperSaiyanWarrior)
    System.out.println("変身訓練の対象です。");

このように、型によって条件分岐したい場面で instanceof は役立ちます。

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

抽象クラスを使うと、サブクラスのオブジェクトを共通の型でまとめられます。

今回なら、SuperSaiyanWarrior と SupportWarrior を Warrior[] 配列でまとめています。

抽象クラスを使う利点内容
共通メソッドを同じ形で呼べるshow() や setSpeed() など
複数のサブクラスを1つの配列で管理できるWarrior[] にまとめられる
多態性を使える実体ごとにメソッドの動きが変わる

一方で、まとめて扱っている中から「このクラスだけを見分けたい」という場面もあります。

そこで instanceof が役立ちます。

ドラゴンボール風にたとえると、まずは全員を Warrior として整列させます。

そのうえで、必要な場面だけ「スーパーサイヤ人戦士は誰か」を見分けるような使い方です。

共通処理は Warrior 型でまとめる。
個別処理が必要なときは instanceof で見分ける。

この組み合わせができると、抽象クラスと多態性をかなり実践的に使えるようになります。

図:抽象クラス型の配列と instanceof の関係

この図が示していること

この図では、抽象クラス Warrior 型の配列で複数のサブクラスをまとめ、必要なときに instanceof で特定の型を見分ける流れを表しています。

Warrior[] には、SuperSaiyanWarrior と SupportWarrior の両方を入れられます。

配列上はどちらも Warrior として扱われます。

しかし、instanceof を使うと、実体が SuperSaiyanWarrior かどうかを判定できます。

対象実体instanceof SuperSaiyanWarrior
warriors[0]SuperSaiyanWarriortrue
warriors[1]SupportWarriorfalse

この図から分かることは、抽象クラス型でまとめる便利さと、必要なときだけ型を見分ける柔軟さを両立できるということです。

instanceof と getClass() の違い

instanceof は、前に学んだ getClass() と少し似ています。

どちらもオブジェクトの型に関係するからです。

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

しくみ役割向いている場面
instanceof特定のクラスかどうかを true または false で判定するif 文で処理を分けたいとき
getClass()そのオブジェクトのクラス情報そのものを取り出す実体のクラス情報を確認したいとき

instanceof は、このオブジェクトは SuperSaiyanWarrior ですか、という質問に true または false で答える道具です。

getClass() は、このオブジェクトのクラス情報は何ですか、という情報を取り出す道具です。

ドラゴンボール風にたとえると、instanceof は「スーパーサイヤ人戦士ですか」と質問する確認札です。

getClass() は「この戦士の所属クラスが書かれた身分証」を取り出すようなものです。

instanceof は if 文で処理を分けたいときに使いやすい

instanceof は true または false を返すため、if 文と相性がよいです。

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

if(warriors[i] instanceof SuperSaiyanWarrior)
    System.out.println("スーパーサイヤ人専用の訓練を行います。");
else
    System.out.println("通常訓練または支援訓練を行います。");

このようにすると、SuperSaiyanWarrior の実体だけに専用処理を行えます。

判定結果処理
trueスーパーサイヤ人専用処理を行う
falseそれ以外の処理を行う

一方、getClass() はクラス情報そのものを取り出すため、デバッグや確認に向いています。

System.out.println(warriors[i].getClass());

このようにすると、class SuperSaiyanWarrior や class SupportWarrior のように、実体のクラス情報を表示できます。

つまり、処理を分けたいなら instanceof。
正体の情報を確認したいなら getClass()。

このように整理すると分かりやすいです。

図:instanceof と getClass() の違い

この図が示していること

この図では、instanceof と getClass() の違いを比較しています。

instanceof は、特定のクラスかどうかを true または false で判定します。

そのため、if 文で処理を分けたいときに向いています。

一方、getClass() は、そのオブジェクトのクラス情報そのものを取り出します。

戻り値は Class クラスのオブジェクトです。

しくみ返すもの使い方の感覚
instanceoftrue または falseこの型ですか、と判定する
getClass()Class オブジェクト実体のクラス情報を取り出す

この図から分かることは、どちらも型に関係するしくみですが、目的が違うということです。

ドラゴンボール風に instanceof を整理する

instanceof は、ドラゴンボール風にたとえると、戦士オブジェクトに対して、

あなたは SuperSaiyanWarrior クラスの戦士ですか

と問いかける演算子です。

SuperSaiyanWarrior なら true。
SupportWarrior なら false。

このように、まとめて扱っているオブジェクトの中から、特定の型を見分けられます。

状況instanceof の役割
Warrior[] に複数の戦士が入っている実体の型を判定できる
SuperSaiyanWarrior だけ処理したいSuperSaiyanWarrior かどうかを調べる
SupportWarrior 以外を分けたいif 文で条件分岐できる
多態性で型が見えにくい実体を確認できる

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

多態性では、いろいろなサブクラスを共通の親型で扱います。

そのため、見た目の型だけでは実体が分かりにくいことがあります。

instanceof を使えば、必要な場面で安全に型を確認し、処理を分けられます。

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

ポイント内容
instanceofオブジェクトが特定のクラスのものか判定する演算子
戻り値true または false
基本形object instanceof ClassName
判定対象変数の宣言型ではなく、実際に指しているオブジェクト
抽象クラスとの関係抽象クラス型でまとめた中から特定のサブクラスを判定できる
多態性との関係まとめて扱う中で実体の型を見分けられる
getClass() との違いinstanceof は判定、getClass() はクラス情報の取得
便利な場面特定クラスだけ処理を分けたいとき

instanceof を理解すると、抽象クラスや多態性でまとめたオブジェクトを、必要な場面で安全に見分けられるようになります。

全員を Warrior として扱いながら、必要なときだけ SuperSaiyanWarrior かどうかを判定する。

この感覚がつかめると、抽象クラス、多態性、型判定のつながりがかなり見えやすくなります。