instanceof とは何か
instanceof は「ある参照が、特定の型(クラスやインターフェース)の実体かどうか」を真偽で判定する演算子です。ポリモーフィズムでは呼び出し側は共通型にそろえますが、特定型だけの追加情報や処理が必要な場面で “型を安全に見極める”ために使います。判定が true のときだけその型として扱う(ダウンキャストする)ことで、ClassCastException を避けられます。
基本の使い方と挙動(重要)
真偽判定のルール
- 親子関係: 参照の実体が判定対象の型に代入可能なら true(子→親や実装→インターフェースで成り立つ)。
- null の扱い: 参照が null の場合は常に false。
- インターフェース: 実体のクラスがそのインターフェースを実装していれば true。
Object x = "hello";
System.out.println(x instanceof String); // true
System.out.println(x instanceof Number); // false
System.out.println(x instanceof CharSequence); // true(String は CharSequence を実装)
x = null;
System.out.println(x instanceof Object); // false(null は常に false)
Java型判定と安全なキャスト(パターンマッチング)
Java 16+ のパターンマッチング for instanceof
判定とキャストを一度に書けます。true のブロック内で“その型の変数”が安全に使えるため、冗長なキャストが不要になります。
void printDetail(Object o) {
if (o instanceof String s) { // 判定+束縛
System.out.println("len=" + s.length());
} else if (o instanceof java.util.List<?> list) {
System.out.println("size=" + list.size());
} else {
System.out.println("unknown");
}
}
JavaJava 15以前でも、判定→キャストの順で安全に書けます。
void printDetailOld(Object o) {
if (o instanceof String) {
String s = (String) o; // 判定後にキャスト
System.out.println("len=" + s.length());
}
}
Javaダウンキャストが必要な典型場面と代替設計(重要な深掘り)
特定型だけの追加情報を扱う
共通型(親やインターフェース)にはないプロパティやメソッドを使うとき、instanceof で型を絞ってから扱います。
abstract class Event { abstract String type(); }
final class ClickEvent extends Event {
private final int x, y;
ClickEvent(int x, int y){ this.x=x; this.y=y; }
@Override String type(){ return "click"; }
int x(){ return x; } int y(){ return y; }
}
final class KeyEvent extends Event {
private final int code;
KeyEvent(int code){ this.code=code; }
@Override String type(){ return "key"; }
int code(){ return code; }
}
void handle(Event e) {
if (e instanceof ClickEvent c) {
System.out.println("Click at " + c.x() + "," + c.y());
} else if (e instanceof KeyEvent k) {
System.out.println("Key code " + k.code());
}
}
Javaなるべく契約へ“昇格”して分岐を減らす(理想形)
instanceof による分岐が増えるなら、共通のインターフェースで契約を定義し、呼び出し側はその契約に依存する形へ整えます。こうすると instanceof が不要になります。
interface Storage { void put(String key, String value); }
final class MemoryStorage implements Storage { /* 省略 */ }
final class FileStorage implements Storage { /* 省略 */ }
void saveAll(Storage st) { st.put("a","1"); st.put("b","2"); } // 分岐なし
Javaよくある落とし穴と回避策(重要)
分岐だらけの可読性低下
型追加のたびに instanceof が増えると、呼び出し側が肥大化します。テンプレートメソッドやダブルディスパッチ(各型が自分の処理を提供)で“違い”を型側へ閉じ込めると、分岐が消えて保守性が上がります。
キャスト前提の思い込みで例外
instanceof を使わず「きっとこの型だ」とキャストすると ClassCastException の原因になります。必ず判定してから扱うか、契約設計を見直してキャスト不要にします。
static、フィールド参照は動的切替の対象外
instanceof は“インスタンスの型”判定です。動的ディスパッチが効くのはインスタンスメソッドのみ。フィールドや static メソッドの呼び出しは参照型に固定されるため、切り替えたい処理はインスタンスメソッドに寄せるのが安全です。
sealed クラスで網羅性を高める(Java 17+)
派生型を permits で限定すると、switch と組み合わせて「全型を網羅」しやすくなります。網羅漏れのバグを減らせます。
public sealed abstract class Shape permits Rect, Circle { abstract double area(); }
public final class Rect extends Shape { /* 省略 */ }
public final class Circle extends Shape { /* 省略 */ }
Java例題で使いどころを体感する
例 1: コレクション内の異種を安全に処理
void summarize(java.util.List<Object> xs) {
for (var o : xs) {
if (o instanceof Number n) {
System.out.println("num=" + n.doubleValue());
} else if (o instanceof String s) {
System.out.println("str=" + s.length());
}
}
}
Java例 2: 例外の種類でハンドリングを分ける
void handle(Exception e) {
if (e instanceof java.io.IOException io) {
System.err.println("IO: " + io.getMessage());
} else if (e instanceof IllegalArgumentException iae) {
System.err.println("ARG: " + iae.getMessage());
} else {
System.err.println("GEN: " + e.getMessage());
}
}
Java仕上げのアドバイス(重要部分のまとめ)
instanceof は「参照の実体が特定型か」を安全に確かめるための演算子で、ダウンキャスト前の必須ガードです。Java 16+ のパターンマッチングで簡潔かつ安全に書けます。ただし、分岐が増えるほど保守性は落ちるため、共通契約(インターフェース)へ“昇格”して呼び出し側の instanceof を減らすのが王道。null は常に false、インターフェースも判定可能、動的切替はインスタンスメソッドのみ——この線引きを覚えておけば、設計もコードも安定します。
