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

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

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

たとえば、鬼滅の刃風にたとえると、Slayer という共通の剣士型を用意して、その配列の中に WaterSlayer や MedicalSlayer のような具体的な剣士オブジェクトを入れられます。

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

ただし、まとめて扱っていると、次のような場面も出てきます。

確認したいこと
この剣士は水の剣士なのかWaterSlayer かどうか
この剣士は支援剣士なのかMedicalSlayer かどうか
特定のクラスだけに処理したいWaterSlayer だけに特別訓練を行う

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

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

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

鬼滅の刃風にたとえると、instanceof は、隊士名簿に並んでいる剣士に対して、
「あなたは WaterSlayer クラスの剣士ですか」
と確認するようなものです。

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

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

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

object instanceof ClassName

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

slayers[i] instanceof WaterSlayer

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

意味
slayers[i] instanceof WaterSlayerslayers[i] の実体が WaterSlayer かどうか調べる
slayers[i] instanceof MedicalSlayerslayers[i] の実体が MedicalSlayer かどうか調べる

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

鬼滅の刃風にたとえると、名簿上は Slayer として並んでいても、本人確認をすれば WaterSlayer なのか MedicalSlayer なのかを判定できる、ということです。

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

なぜ instanceof が必要になるのか

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

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

Slayer[] slayers;

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

配列に入れる実体説明
WaterSlayer水の剣士
MedicalSlayer支援剣士

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

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

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

たとえば、WaterSlayer だけに水の呼吸の追加訓練を行いたい場合です。
また、MedicalSlayer だけに治療技術の確認をさせたい場合もあります。

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

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

鬼滅の刃風に考える instanceof の感覚

鬼滅の刃風にたとえると、instanceof は、隊士に対して、
あなたは本当にこの型の剣士ですか
と確認する札のようなものです。

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

配列要素実体
slayers[0]WaterSlayer
slayers[1]MedicalSlayer

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

slayers[i] instanceof WaterSlayer

すると、WaterSlayer の実体なら true です。
MedicalSlayer の実体なら false です。

実体slayers[i] instanceof WaterSlayer
WaterSlayertrue
MedicalSlayerfalse

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

instanceof の動きを確認する

ファイル名:Sample2.java

abstract class Slayer
{
    protected int speed;

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

    abstract void show();
}

class WaterSlayer extends Slayer
{
    private String name;
    private String breathingStyle;

    public WaterSlayer(String n, String bs)
    {
        name = n;
        breathingStyle = bs;
        System.out.println(name + " 水の呼吸「" + breathingStyle + "」の剣士を作成しました。");
    }

    public void show()
    {
        System.out.println("水の剣士の名前は" + name + "です。");
        System.out.println("呼吸の型は" + breathingStyle + "です。");
        System.out.println("速度は" + speed + "です。");
    }
}

class MedicalSlayer extends Slayer
{
    private String name;
    private String healingSkill;

    public MedicalSlayer(String n, String hs)
    {
        name = n;
        healingSkill = hs;
        System.out.println(name + " 治療技術「" + healingSkill + "」の支援剣士を作成しました。");
    }

    public void show()
    {
        System.out.println("支援剣士の名前は" + name + "です。");
        System.out.println("治療技術は" + healingSkill + "です。");
        System.out.println("速度は" + speed + "です。");
    }
}

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

        slayers[0] = new WaterSlayer("水月", "水流斬り");
        slayers[1] = new MedicalSlayer("胡蝶香", "藤花治療");

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

実行結果

水月 水の呼吸「水流斬り」の剣士を作成しました。
胡蝶香 治療技術「藤花治療」の支援剣士を作成しました。
1番目のオブジェクトはWaterSlayerクラスです。
2番目のオブジェクトはWaterSlayerクラスではありません。

Sample2.java の中心になる部分

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

abstract class Slayer

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

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

Slayer[] slayers;
slayers = new Slayer[2];

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

slayers[0] = new WaterSlayer("水月", "水流斬り");
slayers[1] = new MedicalSlayer("胡蝶香", "藤花治療");

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

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

見た目の型はどちらも Slayer です。
しかし、実体は WaterSlayer と MedicalSlayer で異なります。

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

slayers[i] instanceof WaterSlayer

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

true と false はどう決まるのか

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

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

slayers[0] instanceof WaterSlayer

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

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

slayers[1] instanceof WaterSlayer

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

判定する式結果理由
slayers[0] instanceof WaterSlayertrue実体が WaterSlayer だから
slayers[1] instanceof WaterSlayerfalse実体が MedicalSlayer だから

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

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

鬼滅の刃風にたとえると、隊士名簿には全員 Slayer として並んでいても、実際に札を確認すると、水の剣士なのか支援剣士なのかを見分けられるということです。

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

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

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

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

配列要素見た目の型実体
slayers[0]SlayerWaterSlayer
slayers[1]SlayerMedicalSlayer

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

instanceof は、この実体を見て判定します。
だから、Slayer 型の配列でまとめていても、その中から WaterSlayer だけを見つけることができます。

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

instanceof が便利な場面

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

たとえば、鬼滅の刃風に考えると、次のような場面です。

場面instanceof の使い方
水の剣士だけに特別訓練を行うWaterSlayer かどうか判定する
支援剣士だけに治療準備をさせるMedicalSlayer かどうか判定する
特定クラスだけ別メッセージを出すif 文で処理を分ける
配列の中身の型を確認するループ中に instanceof で調べる

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

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

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

今回なら、WaterSlayer と MedicalSlayer を Slayer[] 配列でまとめています。

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

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

そこで instanceof が役立ちます。

鬼滅の刃風にたとえると、まずは全員を Slayer として整列させます。
そのうえで、必要な場面だけ「水の剣士は誰か」を見分けるような使い方です。

instanceof と getClass() の違い

instanceof は、前に学んだ getClass() と少し似ています。
どちらもオブジェクトの型に関係するからです。

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

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

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

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

鬼滅の刃風にたとえると、instanceof は「水の剣士ですか」と質問する確認札です。
getClass() は「この隊士の所属クラスが書かれた身分証」を取り出すようなものです。

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

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

この図が示していること

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

配列の型は Slayer[] なので、見た目はどちらも Slayer として扱われています。
しかし、実体は WaterSlayer と MedicalSlayer で異なります。

中央では、slayers[i] instanceof WaterSlayer という判定を行っています。

対象実体判定結果
slayers[0]WaterSlayertrue
slayers[1]MedicalSlayerfalse

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

図:instanceof と getClass() の違い

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

この図が示していること

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

instanceof は、特定のクラスかどうかを true または false で判定します。
そのため、if 文で処理を分けたいときに向いています。

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

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

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

鬼滅の刃風に instanceof を整理する

instanceof は、鬼滅の刃風にたとえると、剣士オブジェクトに対して、
あなたは WaterSlayer クラスの剣士ですか
と問いかける演算子です。

WaterSlayer なら true。
MedicalSlayer なら false。

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

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

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

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

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

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

全員を Slayer として扱いながら、必要なときだけ WaterSlayer かどうかを判定する。
この感覚がつかめると、抽象クラス・多態性・型判定のつながりがかなり見えやすくなります。