Instanceof Before Cast
ID |
java.instanceof_before_cast |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Java |
Tags |
CWE:704, reliability |
Description
Reports casts to reference types that are not guarded by a preceding instanceof check. Without such a guard, the cast throws ClassCastException at runtime if the expression does not hold a compatible type.
A cast whose operand is a method call is not reported. (String) request.getAttribute(k),
(LoggerContext) LogManager.getContext(false), (ClipboardManager) getSystemService(..) and
(ThreadPoolExecutor) Executors.newFixedThreadPool(n) are the documented idiom of the API being
called: it returns a general type and the caller names the concrete one. An instanceof test needs
a variable to test, so the advice cannot be followed at the cast site without first introducing a
temporary. A ten-project scan found 1003 of 1634 findings were this shape.
Rationale
When an Object reference is cast to a specific type without first verifying its actual runtime type via instanceof, the cast may fail unexpectedly:
Object o = getElement();
Button b = (Button) o; // ClassCastException if o is not a Button
This is a common source of runtime errors, especially when dealing with heterogeneous collections, deserialized objects, or framework callbacks that pass Object references.
Remediation
Always guard a reference-type cast with an instanceof check:
Object o = getElement();
if (o instanceof Button) {
Button b = (Button) o; // Safe: type verified
b.click();
}
Primitive casts (e.g. (int) doubleValue) are not affected by this rule since they never throw ClassCastException.
Not reported
A cast that provably cannot throw needs no guard, and is not reported:
-
an upcast — to
Object, or to a known supertype of the operand’s declared type ((CharSequence) someString); a redundant cast, where the operand already has the target type; -
a cast to a type variable,
return (S) this;. Type variables are erased at runtime, so the cast is a no-op — and the guard the rule would ask for,this instanceof S, does not compile, so there is no way to act on the finding; -
a cast on a lambda or a method reference,
(ChannelFutureListener) f → {…}. The cast supplies the target type the functional expression is compiled against; it does not downcast a value; -
a cast of the
nullliteral,(String) null, used to pick an overload. Castingnullnever throws, andnull instanceof Xis always false, so a guard would suppress the very call it protects; -
a cast of a class literal,
(Class<?>) Map.class, which is an upcast.
Test code is also skipped: a failed cast there fails the test rather than reaching production. Matching uses the project-relative path against applicativeCode.excludedPathPatterns — configure additional exclusions in depsdoctor.yml.