Upcasting and Downcasting in Java: Casting Objects - Quiz
Total: 6 questions
1. What are upcasting and downcasting in Java, and how do they differ?
What are upcasting and downcasting in Java, and how do they differ?
Upcasting (a widening reference conversion) casts a reference from a subclass to a superclass or interface. It is implicit and always safe:
Box6 box = new HeavyBox1(); // upcasting
Downcasting (a narrowing reference conversion) casts from a superclass or interface to a subclass. It needs an explicit (Type) cast operator and may throw a ClassCastException:
HeavyBox1 heavy = (HeavyBox1) box; // downcasting
The object itself never changes. Only the type of the reference changes, and with it the set of fields and methods you can access. After upcasting, a Box6 reference sees only the members of Box6, but overridden methods are still called based on the real class of the object (polymorphism). Downcasting gives you access to subclass members again, such as the weight field.
2. When is a ClassCastException thrown, and why can't the compiler prevent it?
When is a ClassCastException thrown, and why can't the compiler prevent it?
A ClassCastException is thrown at runtime when, during an explicit cast, the real object is not an instance of the target type:
Box6 box = new ColorBox();
HeavyBox1 heavy = (HeavyBox1) box; // compiles, but throws ClassCastException
The compiler knows only the declared type of the variable (Box6) and checks only that the cast is possible in principle: HeavyBox1 is a subclass of Box6. Which object the variable actually holds is known only to the JVM at runtime. If the types are clearly incompatible (for example, (String) box), it is a compile error.
How to avoid it: check the type with instanceof before downcasting, use pattern matching in Java 16+ (if (box instanceof HeavyBox1 h)), or better yet, design your code so the behavior you need is available through methods of the superclass or interface.
3. What does the instanceof operator check, and what are its rules?
What does the instanceof operator check, and what are its rules?
obj instanceof Type returns true if the reference is not null and the object is an instance of Type, of its subclass, or of a class that implements the Type interface. In other words, true means the cast (Type) obj will succeed without a ClassCastException.
- For
nullthe result is alwaysfalse. The cast(HeavyBox1) nullitself is allowed and returnsnull. - The left operand must be a reference type; with primitives (
int x; x instanceof Integer) the code doesn't compile. - If the variable's class and the checked class are clearly unrelated by inheritance, it is a compile error, not
false. - An array is an object too:
new int[5] instanceof Objectistrue.
4. What is pattern matching for instanceof in Java 16+, and how does it replace an explicit cast?
What is pattern matching for instanceof in Java 16+, and how does it replace an explicit cast?
Since Java 16 (JEP 394), you can combine the type check and the cast: after instanceof you declare a variable that already has the right type.
// before Java 16
if (box instanceof HeavyBox1) {
HeavyBox1 heavy = (HeavyBox1) box;
System.out.println(heavy.weight);
}
// Java 16+
if (box instanceof HeavyBox1 heavy && heavy.weight > 10) {
System.out.println(heavy.weight);
}
The pattern variable is in scope only where the check is guaranteed to have passed: inside the if branch, to the right of &&, and after if (!(obj instanceof T t)) return; in the rest of the method. It is not in scope to the right of ||, which is a compile error. Java 21 brings the same idea to switch (pattern matching for switch): case HeavyBox1 h -> ....
5. Why does a cast to an interface compile even if the class doesn't implement it, but not for a final class?
Why does a cast to an interface compile even if the class doesn't implement it, but not for a final class?
Casting between classes is allowed only within one hierarchy: (String) box for Box6 box is a compile error. The rules for interfaces are looser. If the class is not final, a subclass might exist that implements the interface, so the compiler allows the cast and defers the check to runtime:
Box6 box = new Box6();
Runnable r = (Runnable) box; // compiles, ClassCastException at runtime
// Runnable r2 = (Runnable) "abc"; // compile error
String is a final class and doesn't implement Runnable. It can't have subclasses, so the cast is impossible, and the compiler reports an error right away.
6. Which array type conversions are allowed in Java, and what is an ArrayStoreException?
Which array type conversions are allowed in Java, and what is an ArrayStoreException?
- Not allowed: an array to a primitive and back;
int[]toInteger[]and back (autoboxing doesn't apply to arrays);int[]tolong[], even thoughintwidens tolong. - Allowed:
HeavyBox1[]toBox6[]implicitly (upcasting); the reverse with an explicit cast, checked at runtime (ClassCastExceptionif the real array is aBox6[]). - Any array can be cast to
Object,CloneableandSerializable.
Arrays in Java are covariant, but they remember the real type of their elements. So storing an object of the wrong type throws an ArrayStoreException:
Box6[] box = new HeavyBox1[4];
box[0] = new Box6(); // ArrayStoreException
Generics work differently: a List<HeavyBox1> can't be assigned to a List<Box6>, and the error is caught at compile time.