The Java instanceof operator checks whether an object is an instance of a given class or interface, including its subclasses and implementing classes, and returns true or false. Since Java 16, instanceof can also declare a pattern variable, so the check and the cast happen in one step.
We use instanceof before a cast to avoid a ClassCastException, in equals() methods, and when code receives a general type such as Object and must handle a few concrete types differently.
The following example checks one Object variable against several types and uses the pattern form to read the string length.
Object value = "hello";
boolean isText = value instanceof String; // true
boolean isChars = value instanceof CharSequence; // true, String implements it
boolean isNumber = value instanceof Integer; // false
Object missing = null;
boolean nullCheck = missing instanceof String; // false, never throws
int length = value instanceof String s ? s.length() : 0; // 5
Notice that instanceof returns false for null instead of throwing an exception, and that the pattern variable s is already a String. We cover the syntax and the compile-time checks, pattern matching with && and negation, arrays and generics, the move to switch patterns, and how instanceof compares with getClass() and Class.isInstance().
1. How the instanceof Check Works
The left operand of instanceof is an expression of a reference type, and the right operand is a type. At runtime, the operator returns true when the value is not null and could be cast to that type without a ClassCastException. That covers the class of the object itself, all its superclasses up to Object, and every interface the class or its superclasses implement.

List<String> tags = new ArrayList<>();
boolean isList = tags instanceof List; // true
boolean isCollection = tags instanceof Collection; // true
boolean isAbstract = tags instanceof AbstractList; // true
boolean isRandom = tags instanceof RandomAccess; // true
boolean isLinked = tags instanceof LinkedList; // false
The check is about the object, not the variable. The variable tags is declared as List, but the object is an ArrayList, so every supertype of ArrayList matches. The full rules are in JLS 15.20.2.
1.1. No null Check Needed
An expression x instanceof T is always false when x is null. So a separate null check before instanceof adds nothing.
static boolean isBlankText(Object input) {
return input instanceof String text && text.isBlank();
}
boolean blank = isBlankText(" "); // true
boolean notText = isBlankText(42); // false
boolean nothing = isBlankText(null); // false
Writing input != null && input instanceof String is a common habit from older code, and static analysis tools such as SonarQube flag it as redundant.
1.2. Checks the Compiler Rejects
The compiler refuses an instanceof check that can never be true. If the type of the expression and the target type cannot have a common object, the code does not compile, which catches mistakes early.
// does not compile: incompatible types: String cannot be converted to Integer
String code = "42";
boolean isInt = code instanceof Integer;
The same error appears for a final class and an interface it does not implement, such as Integer and Runnable, because no subclass of Integer can ever exist. For a non-final class and any interface, the check compiles, since a subclass could implement the interface.
2. Pattern Matching for instanceof
Before Java 16, every successful check needed a cast on the next line, which repeated the type and invited copy-paste errors. Pattern matching for instanceof was a preview in Java 14 and 15 and became a standard feature in Java 16 with JEP 394. The pattern value instanceof String s tests the type and, when it matches, assigns the value to a new variable s of type String.
// before Java 16
static int oldLength(Object value) {
if (value instanceof String) {
String s = (String) value;
return s.length();
}
return 0;
}
// Java 16 and later
static int newLength(Object value) {
if (value instanceof String s) {
return s.length();
}
return 0;
}
int before = oldLength("tea"); // 3
int after = newLength("coffee"); // 6
int other = newLength(3.5); // 0
A detailed look at the pattern form, including record patterns, is in our article on pattern matching for instanceof.
2.1. Where the Pattern Variable Is in Scope
The pattern variable exists only where the compiler can prove that the match succeeded. This is called flow scoping. With &&, the variable is usable on the right side, and with a negated check that returns early, it is usable after the if statement.
static String describe(Object input) {
if (!(input instanceof Integer number)) {
return "not a number";
}
return number > 100 ? "large" : "small"; // number is in scope here
}
String big = describe(250); // "large"
String text = describe("250"); // "not a number"
With ||, the variable is not in scope on the right side, because that side runs only when the match failed. The code input instanceof Integer n || n > 0 fails with cannot find symbol.
2.2. A Real-World Case in equals()
Hand-written equals() methods are the most common place for instanceof in application code. A shopping cart compares CartItem objects when the same product is added twice, and the pattern form makes the method short and null-safe.
class CartItem {
private final String sku;
private final int quantity;
CartItem(String sku, int quantity) {
this.sku = sku;
this.quantity = quantity;
}
@Override
public boolean equals(Object other) {
return other instanceof CartItem item
&& sku.equals(item.sku)
&& quantity == item.quantity;
}
@Override
public int hashCode() {
return Objects.hash(sku, quantity);
}
}
CartItem first = new CartItem("pen", 2);
boolean same = first.equals(new CartItem("pen", 2)); // true
boolean otherType = first.equals("pen"); // false
boolean nullSafe = first.equals(null); // false
An instanceof check in equals() also accepts subclasses of CartItem. When a subclass adds fields, that breaks symmetry, so classes meant for subclassing often compare with getClass() instead, as shown in section 5.
3. Using instanceof With Arrays and Generics
Arrays are objects, so instanceof works with them too. A primitive array is an instance of its own type and of Object, Cloneable and Serializable. An object array is also an instance of the array types of its element’s supertypes, because Java arrays are covariant.
int[] scores = {90, 75};
boolean primitive = scores instanceof int[]; // true
boolean asObject = scores instanceof Object; // true
Integer[] boxed = {90, 75};
boolean numbers = boxed instanceof Number[]; // true
boolean objects = boxed instanceof Object[]; // true
Generics work differently because type arguments are erased at runtime. A check such as obj instanceof List<String> on an Object does not compile and reports Object cannot be safely cast to List<String>. We check against the raw or wildcard type and test the elements, or we use a generic type in the pattern only when the compiler already knows it is safe.
Object payload = List.of("a", "b");
boolean anyList = payload instanceof List<?>; // true
List<String> names = new ArrayList<>(List.of("x"));
boolean safe = names instanceof ArrayList<String> list && list.size() == 1; // true
Since Java 16, the second form is allowed because a List<String> can only be an ArrayList<String> if it is an ArrayList at all, so no unchecked cast happens.
4. From instanceof Chains to switch Patterns
A long if–else chain of instanceof checks is hard to read, and the compiler cannot tell when a new type is missing. Since Java 21, a switch can match type patterns and record patterns (JEP 441, JEP 440). Combined with a sealed interface, the compiler checks that every type is handled.
The following example is a notification service that sends three kinds of messages. Each message type is a record that implements a sealed interface.
sealed interface Message permits Email, Sms, Push {}
record Email(String to, String subject) implements Message {}
record Sms(String phone, String text) implements Message {}
record Push(String device) implements Message {}
static String route(Message message) {
return switch (message) {
case Email e -> "mail to " + e.to();
case Sms(String phone, String text) when text.length() > 160 -> "split sms to " + phone;
case Sms(String phone, String text) -> "sms to " + phone;
case Push p -> "push to " + p.device();
};
}
String mail = route(new Email("ana@example.com", "Hi")); // "mail to ana@example.com"
String sms = route(new Sms("555-0100", "On my way")); // "sms to 555-0100"
String push = route(new Push("pixel")); // "push to pixel"
For a fixed set of types, a switch over a sealed type is safer than an instanceof chain, because adding a fourth record breaks the build until route() handles it. The plain instanceof operator stays the better fit for a single check, such as in equals().
Java 25 also previews primitive types in patterns, instanceof and switch (JEP 507). With –enable-preview, a check such as value instanceof int n tests whether a value fits into an int without loss. The feature can still change, so we keep it out of production code.
5. instanceof vs getClass() vs Class.isInstance()
Java has three ways to test the type of an object, and they differ in how they treat subclasses and when the type is known.
| Check | Matches subclasses | Type known at | null input | Typical use |
|---|---|---|---|---|
| obj instanceof T | Yes | Compile time | false | Casting, equals(), type dispatch |
| obj.getClass() == T.class | No, exact class only | Compile time | NullPointerException | Strict equals() in class hierarchies |
| type.isInstance(obj) | Yes | Runtime, from a Class object | false | Generic code, frameworks, stream filters |
Object number = 7;
boolean pattern = number instanceof Number; // true
boolean exact = number.getClass() == Number.class; // false, class is Integer
Class<?> wanted = Number.class;
boolean dynamic = wanted.isInstance(number); // true
List<Object> mixed = List.of(1, "two", 3.0);
List<Integer> ints = mixed.stream().filter(Integer.class::isInstance).map(Integer.class::cast).toList(); // [1]
The method Class.isInstance() is the runtime version of instanceof, and together with Class.cast() it filters a mixed stream by type. Both instanceof and isInstance() belong to the type system that object-oriented programming in Java is built on, and they let us work with an object through any of its types.
6. Java instanceof FAQs
People who read older code with instanceof chains ask about speed, style and versions first.
6.1. Is instanceof a Keyword or an Operator?
Both. The word instanceof is a reserved keyword, and the expression it forms is called the type comparison operator. It has the same precedence as the relational operators < and >.
6.2. Is Using instanceof Bad Practice?
No, but a long chain of instanceof checks over the same variable often means the behavior belongs in the classes themselves. In that case we move it into an overridden method, or we use a switch over a sealed type so the compiler checks that every case is covered. A single check in equals() or before a cast is normal code.
6.3. Is instanceof Slow?
No. The JIT compiler turns most instanceof checks into a few machine instructions that compare class metadata, so we choose between instanceof and polymorphism for design reasons, not speed.
6.4. Which Java Version Added Pattern Matching for instanceof?
Java 16, through JEP 394. It was a preview feature in Java 14 and 15, so code from those versions needed –enable-preview. Record patterns inside instanceof became standard in Java 21.
6.5. Can the Pattern Variable Be Reassigned?
Yes, a pattern variable behaves like a local variable and can be reassigned, unless we declare it final, as in obj instanceof final String s. Reassigning it is legal but confusing, so we avoid it.
7. Conclusion
The instanceof operator answers one question, whether an object can be treated as a given type. It returns false for null, matches every superclass and interface of the object, and does not compile when the answer could never be true.
Since Java 16, the pattern form combines the check and the cast, and flow scoping makes the variable available after a negated check or on the right side of &&. For a single type check, such as in equals(), instanceof is the right tool. For several related types, a switch with type and record patterns over a sealed interface is shorter and checked by the compiler.
8. References
- JLS 25, The instanceof Operator
- JEP 394, Pattern Matching for instanceof
- JEP 441, Pattern Matching for switch
- JEP 507, Primitive Types in Patterns, instanceof, and switch (Third Preview)
- Oracle, Pattern Matching for instanceof
Happy Learning !!