型チェックの基本!instanceof演算子を使いこなそう

docs

前回は「ポリモーフィズム:オブジェクト指向の強力な機能 | ToolDocs」について学んだよね。今回はその多態性と密接に関わる「instanceof演算子」について、楽しく学んでいこう!

instanceof演算子って何者?

簡単に言うと、instanceof演算子は「このオブジェクトは、あのクラスのインスタンス(またはそのサブクラスのインスタンス)なの?」という疑問に答えてくれる魔法の言葉なんだ。オブジェクトの「型」をチェックするときに使う、とっても便利なツールだよ。

例えば、動物園に色々な動物がいるとするよね。

Java

class Animal {
    void makeSound() {
        System.out.println("動物の鳴き声");
    }
}

class Dog extends Animal {
    void makeSound() {
        System.out.println("ワンワン!");
    }
    void wagTail() {
        System.out.println("しっぽを振る");
    }
}

class Cat extends Animal {
    void makeSound() {
        System.out.println("ニャーニャー!");
    }
    void scratch() {
        System.out.println("爪とぎをする");
    }
}

public class Zoo {
    public static void main(String[] args) {
        Animal myDog = new Dog();
        Animal myCat = new Cat();
        Animal unknownAnimal = new Animal();

        // ここでinstanceofの出番!
        System.out.println("myDogはDogのインスタンス? " + (myDog instanceof Dog));   // true
        System.out.println("myCatはCatのインスタンス? " + (myCat instanceof Cat));   // true
        System.out.println("myDogはCatのインスタンス? " + (myDog instanceof Cat));   // false
        System.out.println("unknownAnimalはAnimalのインスタンス? " + (unknownAnimal instanceof Animal)); // true
        System.out.println("unknownAnimalはDogのインスタンス? " + (unknownAnimal instanceof Dog)); // false
    }
}

こんな感じで、instanceoftruefalseで答えてくれるんだ。

vscodeで実行した結果

なんでinstanceofが必要なの?多態性とどう関係するの?

前回の「多態性(ポリモーフィズム)」で、Animal myDog = new Dog();のように、スーパークラスの型でサブクラスのオブジェクトを参照できるって話をしたよね。これはすごく便利なんだけど、一つ問題が出てくることがあるんだ。

例えば、myDogAnimal型の変数だけど、実際にはDogオブジェクトだよね。このmyDogに対して、Dogクラスにしかない特別なメソッド(例えばwagTail())を呼び出したい場合、どうする?

Java

Animal myAnimal = new Dog();
// myAnimal.wagTail(); // コンパイルエラー!AnimalクラスにはwagTail()がないからね。

ここでinstanceofの出番!「もしmyAnimalDogだったら、wagTail()を呼んであげよう」という条件分岐をするときに使うんだ。

Java

if (myAnimal instanceof Dog) {
    Dog actualDog = (Dog) myAnimal; // Dog型にキャスト!
    actualDog.wagTail();
} else if (myAnimal instanceof Cat) {
    Cat actualCat = (Cat) myAnimal; // Cat型にキャスト!
    actualCat.scratch();
} else {
    System.out.println("この動物は特別なアクションができないみたい。");
}

このように、instanceofでオブジェクトの型をチェックしてから、その型に合った処理を行うことで、より柔軟なプログラムが書けるようになるんだ。これを「ダウンキャスト」と呼ぶよ。安全にダウンキャストを行うためには、instanceofで型をチェックすることがとっても重要なんだ。

vscodeで実行した結果

instanceofとnullの関係

instanceof演算子の左側がnullの場合、結果は常にfalseになるよ。これは覚えておこう!

Java

Dog myDog = null;
System.out.println("myDogはDogのインスタンス? " + (myDog instanceof Dog)); // false

Java 14以降のinstanceofの進化!パターンマッチング

Java 14からは、instanceof演算子がさらに便利になったんだ!「パターンマッチング」という機能が追加されて、より簡潔に書けるようになったよ。

さっきの動物の例をもう一度見てみよう。

Java

public class ZooModern {
    public static void main(String[] args) {
        Animal myAnimal = new Dog(); // または Cat(); または Animal();

        if (myAnimal instanceof Dog dog) { // ここが新しい!
            dog.wagTail(); // もうキャスト不要!dog変数として使える!
        } else if (myAnimal instanceof Cat cat) { // ここも新しい!
            cat.scratch(); // cat変数として使える!
        } else {
            System.out.println("この動物は特別なアクションができないみたい。");
        }
    }
}

どう?すごくスッキリしたでしょ?if (myAnimal instanceof Dog dog)と書くことで、myAnimalDogのインスタンスだった場合に、自動的にDog型のdogという新しい変数が宣言されて、すぐにその変数を使えるようになるんだ。これによって、冗長なキャストコードを書かずに済むようになったんだ。

instanceofを使う上での注意点

  • 無駄なinstanceofは避ける: instanceofを多用すると、プログラムが読みにくくなったり、拡張性が下がったりすることがあるんだ。多くの場合、ポリモーフィズム(多態性)をうまく使うことで、instanceofを使わずに済むケースも多いよ。
    • 例えば、すべての動物が特定の行動(例: interact())を持つように、Animalインターフェースや抽象クラスで定義しておけば、instanceofを使わずにmyAnimal.interact()と呼ぶだけで済むこともあるよね。
  • 継承関係を理解する: instanceofは、そのクラス自身だけでなく、そのサブクラスのインスタンスに対してもtrueを返すよ。

Java

class Vehicle {} 

class Car extends Vehicle {} 

class SportsCar extends Car {} 

public class TypeCheck { 
    public static void main(String[] args) { 
        SportsCar mySportsCar = new SportsCar(); 
        System.out.println("mySportsCarはSportsCarのインスタンス? " + (mySportsCar instanceof SportsCar)); // true 
        System.out.println("mySportsCarはCarのインスタンス? " + (mySportsCar instanceof Car)); // true 
        System.out.println("mySportsCarはVehicleのインスタンス? " + (mySportsCar instanceof Vehicle)); // true
  }
}

このように、継承ツリーの上位にいるクラスに対してもtrueになることに注意しよう。

vscodeで実行した結果

instanceofとインターフェース

instanceof演算子は、クラスだけでなくインターフェースに対しても使うことができるよ。

Java

interface Flyable {
    void fly();
}

class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("鳥が飛ぶ");
    }
}

class Airplane implements Flyable {
    @Override
    public void fly() {
        System.out.println("飛行機が飛ぶ");
    }
}

class Dog extends Animal { // Animalクラスの例を再利用
    // 犬は飛ばないのでFlyableを実装しない
}

public class FlyingCheck {
    public static void main(String[] args) {
        Flyable myBird = new Bird();
        Animal myDog = new Dog();

        if (myBird instanceof Flyable) {
            System.out.println("myBirdは飛べる!"); // 出力される
        }

        if (myDog instanceof Flyable) {
            System.out.println("myDogは飛べる!"); // 出力されない
        }
    }
}

このように、あるオブジェクトが特定のインターフェースを実装しているかどうかをチェックするのにも使えるんだ。

vscodeで実行した結果

まとめ

今回はJavaのinstanceof演算子について学んだね。

  • instanceofは、オブジェクトが特定のクラス(またはそのサブクラス)のインスタンスであるか、あるいは特定のインターフェースを実装しているかをチェックする演算子だよ。
  • 多態性と合わせて使うことで、ダウンキャストを安全に行うことができるんだ。
  • Java 14以降では、パターンマッチングが導入されて、より簡潔に型チェックとキャストを同時に行えるようになったよ。
  • nullに対してinstanceofを使うとfalseになることを覚えておこう。
  • instanceofの多用は避け、ポリモーフィズムで解決できる場合はそちらを優先しよう。

これで、オブジェクトの型をしっかりチェックして、安全にプログラムを組めるようになったはず!次の記事では、さらにJavaの奥深い世界を探求していくよ。お楽しみに!

関連リンク

コメント

タイトルとURLをコピーしました