instanceof Operator in Java – Type Checks and Pattern Matching
The instanceof operator tests whether an object is an instance of a given type: a class, a subclass of it, or a class implementing an interface. It returns a boolean and is most often used before a downcast to make the cast safe.
Modern Java has transformed instanceof: since Java 16 it can declare a variable while testing (pattern matching), and Java 21 adds record patterns and pattern matching for switch. This tutorial covers the classic operator and all of these features.
1. Syntax and Basic Use
expression instanceof Type // classic form: true or false
expression instanceof Type name // pattern matching (Java 16+)
expression instanceof Record(var a, var b) // record pattern (Java 21+)
import java.util.*;
public class InstanceofBasics {
static class Animal { }
static class Dog extends Animal { }
static class Cat extends Animal { }
public static void main(String[] args) {
Animal a = new Dog();
System.out.println("a instanceof Dog: " + (a instanceof Dog)); // actual object is a Dog
System.out.println("a instanceof Animal: " + (a instanceof Animal)); // subclass IS-A parent
System.out.println("a instanceof Object: " + (a instanceof Object)); // everything is an Object
System.out.println("a instanceof Cat: " + (a instanceof Cat));
List<String> list = new ArrayList<>();
System.out.println("list instanceof Collection: " + (list instanceof Collection));
System.out.println("list instanceof RandomAccess: " + (list instanceof RandomAccess));
Animal nothing = null;
System.out.println("null instanceof Animal: " + (nothing instanceof Animal)); // always false
}
}
a instanceof Dog: true a instanceof Animal: true a instanceof Object: true a instanceof Cat: false list instanceof Collection: true list instanceof RandomAccess: true null instanceof Animal: false
instanceofchecks the runtime type of the object, not the declared type of the variable.- It returns true for the object's class, all its superclasses and all interfaces it implements.
null instanceof AnyTypeis alwaysfalse, so no null check is needed.- If the types can never match (for example
StringandInteger), the code does not compile.
2. Safe Downcasting
Casting a reference to a subtype throws ClassCastException if the object is not of that type. Testing with instanceof first prevents it. The classic pattern requires a test and a separate cast:
if (obj instanceof String) {
String s = (String) obj; // repeated type, extra cast
System.out.println(s.length());
}
3. Pattern Matching for instanceof (Java 16)
The pattern form tests and casts in one step, binding a new variable that is in scope only where the test is known to be true:
public class PatternMatching {
static String describe(Object o) {
// Java 16+: test and cast in one step; 's', 'i', 'arr' are pattern variables
if (o instanceof String s && !s.isEmpty()) {
return "String of length " + s.length();
} else if (o instanceof Integer i) {
return "Integer, doubled = " + (i * 2);
} else if (o instanceof int[] arr) {
return "int array with " + arr.length + " elements";
}
return "something else: " + o;
}
public static void main(String[] args) {
System.out.println(describe("Java"));
System.out.println(describe(21));
System.out.println(describe(new int[] {1, 2, 3}));
System.out.println(describe(""));
System.out.println(describe(3.14));
}
}
String of length 4 Integer, doubled = 42 int array with 3 elements something else: something else: 3.14
- The pattern variable can be used in the rest of a
&&condition (s.isEmpty()above), but not after||. - With
if (!(o instanceof String s)) return;,sis in scope after the if statement (flow scoping). - Pattern variables are not implicitly final, but reassigning them is discouraged.
4. Record Patterns (Java 21)
Record patterns go further: they test the type and extract the record's components. This also makes equals implementations much shorter:
public class OldVsNew {
record Point(int x, int y) { }
// Before Java 16: test, then cast
static boolean equalsOld(Point p, Object o) {
if (!(o instanceof Point)) return false;
Point other = (Point) o;
return p.x() == other.x() && p.y() == other.y();
}
// Java 16+: pattern matching
static boolean equalsNew(Point p, Object o) {
return o instanceof Point other && p.x() == other.x() && p.y() == other.y();
}
// Java 21: record patterns deconstruct the object directly
static String where(Object o) {
if (o instanceof Point(int x, int y) && x == y) return "on the diagonal at " + x;
if (o instanceof Point(int x, int y)) return "at (" + x + ", " + y + ")";
return "not a point";
}
public static void main(String[] args) {
Point p = new Point(2, 3);
System.out.println(equalsOld(p, new Point(2, 3)) + " " + equalsNew(p, new Point(2, 3)) + " " + equalsNew(p, "x"));
System.out.println(where(new Point(4, 4)));
System.out.println(where(p));
System.out.println(where(42));
}
}
true true false on the diagonal at 4 at (2, 3) not a point
5. Pattern Matching for switch (Java 21)
Long chains of if (x instanceof A) ... else if (x instanceof B) can be replaced by a switch with type patterns. Guards (when) add conditions, and with a sealed type the compiler checks that every case is covered:
public class SwitchPatterns {
sealed interface Payment permits Card, Upi, Cash { }
record Card(String last4, double amount) implements Payment { }
record Upi(String vpa, double amount) implements Payment { }
record Cash(double amount) implements Payment { }
// Instead of a chain of instanceof checks: pattern matching switch (Java 21)
static String receipt(Payment p) {
return switch (p) {
case Card c when c.amount() > 10_000 -> "Card ****" + c.last4() + " (OTP required)";
case Card c -> "Card ****" + c.last4();
case Upi u -> "UPI " + u.vpa();
case Cash c -> "Cash " + c.amount();
};
}
public static void main(String[] args) {
System.out.println(receipt(new Card("4242", 25_000)));
System.out.println(receipt(new Card("1881", 999)));
System.out.println(receipt(new Upi("asha@upi", 450)));
System.out.println(receipt(new Cash(200)));
}
}
Card ****4242 (OTP required) Card ****1881 UPI asha@upi Cash 200.0
6. When to Avoid instanceof
Frequent instanceof checks on your own class hierarchy can be a sign that behaviour belongs in the classes themselves. Consider:
- Polymorphism: define a method in the parent type and override it in each subclass.
- Sealed types with switch patterns: when operations are better kept outside the classes, the compiler still guarantees exhaustiveness.
instanceof remains the right tool in equals methods, when handling values of type Object, and when working with types you do not control. See Polymorphism.
7. instanceof vs Class.isInstance() vs getClass()
| Check | Includes subtypes? | Type known at |
|---|---|---|
obj instanceof Type | Yes | Compile time |
type.isInstance(obj) | Yes | Run time (a Class object) |
obj.getClass() == Type.class | No: exact class only | Compile time |
8. Summary
instanceoftests an object's runtime type against a class or interface, including supertypes.- It returns
falsefornulland is used to make downcasts safe. - Java 16 pattern matching tests and casts in one step; Java 21 adds record patterns and switch patterns.
- Prefer polymorphism or sealed-type switches over long
instanceofchains.
Related Tutorials
Frequently Asked Questions (FAQ)
Q1. What is the instanceof operator in Java?
A binary operator that returns true if an object is an instance of a given class, one of its subclasses, or an implementation of a given interface.
Q2. What does instanceof return for null?
Always false. It never throws a NullPointerException.
Q3. What is pattern matching for instanceof?
A Java 16 feature that lets you write if (o instanceof String s), testing the type and binding a typed variable in one step.
Q4. Does instanceof check the declared or the actual type?
The actual runtime type of the object.
Q5. Can instanceof be used with interfaces?
Yes. It returns true if the object's class implements the interface directly or indirectly.
Q6. Why do I get a compile error with instanceof?
If the expression's type and the target type can never be related, such as a String tested against Integer, the compiler rejects the check.
Q7. Can I use instanceof with generics?
Only with raw or wildcard types, such as obj instanceof List<?>. Type arguments are erased at run time, so instanceof List<String> is generally not allowed.
Q8. What is the scope of a pattern variable?
Wherever the match is guaranteed: the true branch, later parts of an && expression, or after an if that returns when the match fails.
Q9. What is a record pattern?
A Java 21 pattern such as o instanceof Point(int x, int y) that matches a record type and extracts its components.
Q10. Can switch be used instead of instanceof chains?
Yes. Since Java 21, switch supports type patterns, record patterns and when guards.
Q11. Can instanceof be used with arrays?
Yes, for example o instanceof int[] or o instanceof String[] arr.
Q12. Can instanceof be used with primitives?
Not in Java 21. Primitive patterns are a preview feature in later releases; with an Object, test the wrapper type such as Integer.
Q13. What is the difference between instanceof and getClass()?
instanceof accepts subclasses; getClass() == X.class matches only the exact class.
Q14. What is Class.isInstance()?
The dynamic equivalent of instanceof, used when the type is only known at run time as a Class object.
Q15. Is using instanceof bad practice?
Not inherently. It is appropriate in equals and with Object values, but frequent checks on your own hierarchy suggest polymorphism would be cleaner.
Q16. How does instanceof help avoid ClassCastException?
By confirming the object's type before casting, so the cast cannot fail.
Q17. Can a pattern variable shadow a field?
Yes. It behaves like a local variable and can hide a field with the same name, which can be confusing.
Q18. What is a guard in switch patterns?
A when clause that adds a boolean condition to a case, for example case Card c when c.amount() > 10_000.
Q19. Does instanceof affect performance?
It is very fast; the JIT optimises type checks heavily. Design concerns matter far more than speed.
Q20. Is instanceof a keyword?
Yes. It is a reserved keyword used only as this operator.