The instanceof Type Comparison Operator
The binary instanceof operator can be used for comparing types. It has the following syntax when used as a type comparison operator (note that the keyword is composed of lowercase letters only):
reference_expression
instanceof
destination_type
The instanceof type comparison operator returns true if the left-hand operand (i.e., the reference value that results from the evaluation of reference expression) can be a subtype of the right-hand operand (destination type). It always returns false if the left-hand operand is null. If the instanceof operator returns true, the corresponding type cast expression (destination_type) applied to the left-hand operand reference_expression will always be valid:
(
destination_type
)
reference_expression
Both the type cast expression and the instanceof type comparison operator require a compile-time check and a runtime check. The compile-time check determines whether there is a subtype–supertype relationship between the source and destination types. Given that the type of the reference expression is source type, the compiler determines whether a reference of source type and a reference of destination type can refer to objects of a reference type that is a common subtype of both source type and destination type in the type hierarchy. If this is not the case, then obviously there is no relationship between the types, and neither the cast nor the instanceof type comparison operator application would be valid. At runtime, the reference expression evaluates to a reference value of an object. The instanceof type comparison operator determines whether the type of this object is a subtype of the destination type.
With the classes Light and String as source type and destination type, respectively, there is no subtype–supertype relationship between source type and destination type. The compiler would reject casting a reference of type Light to type String or applying the instanceof operator, as shown at (2) and (3) in Example 5.21. References of the classes Light and TubeLight can refer to objects of the class TubeLight (or its subclasses) in the inheritance hierarchy depicted in Figure 5.1, p. 194. Therefore, it makes sense to apply the instanceof type comparison operator or to cast a reference of the type Light to the type TubeLight as shown at (4) and (5), respectively, in Example 5.21.
At runtime, the result of applying the instanceof type comparison operator at (4) is false because the reference light1 of the class Light will actually denote an object of the subclass LightBulb, and this object cannot be denoted by a reference of the peer class TubeLight. Applying the cast at (5) results in a ClassCastException for the same reason. This is the reason why cast conversions are said to be unsafe, as they may throw a ClassCastException at runtime. Note that if the result of the instanceof type comparison operator is false, the cast involving the operands will throw a ClassCastException.
In Example 5.21, the result of applying the instanceof type comparison operator at (6) is also false because the reference light1 will still denote an object of the class LightBulb, whose objects cannot be denoted by a reference of its subclass SpotLightBulb. Thus applying the cast at (7) causes a ClassCastException to be thrown at runtime.
The situation shown at (8), (9), and (10) illustrates typical usage of the instanceof type comparison operator to determine which object a reference is denoting so that it can be cast for the purpose of carrying out some specific action—as we will see later, the instanceof pattern match operator provides a better solution for this particular case (p. 274). The reference light1 of the class Light is initialized to an object of the subclass NeonLight at (8). The result of the instanceof type comparison operator at (9) is true because the reference light1 will denote an object of the subclass NeonLight, whose objects can also be denoted by a reference of its superclass TubeLight. By the same token, the cast at (10) is valid. If the result of the instanceof type comparison operator is true, the cast involving the operands will be valid as well.
So far in Example 5.21, the static type of the reference in the left-hand operand of the instanceof comparison operator has been a supertype of the destination type. At (12), the static type (SpotLightBulb) of the reference in the left-hand operand of the instanceof comparison operator is a subtype of the destination type (Light). Since the dynamic type of the object is SpotLightBulb, which is a subtype of the destination type Light, the instanceof comparison operator returns true. The reference value of the object can be trivially assigned to a reference of the supertype Light, and the cast shown at (13) is actually redundant. This behavior of the instanceof comparison operator is allowed because of backward compatibility.
Example 5.21 The instanceof Type Comparison and Cast Operators
// See
Figure 5.1
,
p. 194
, for inheritance hierarchy.
class Light { /* … */ }
class LightBulb extends Light { /* … */ }
class SpotLightBulb extends LightBulb { /* … */ }
class TubeLight extends Light { /* … */ }
class NeonLight extends TubeLight { /* … */ }
public class WhoAmI {
public static void main(String[] args) {
boolean result1, result2, result3, result4;
Light light1 = new LightBulb(); // (1)
// String str = (String) light1; // (2) Compile-time error!
// result1 = light1 instanceof String; // (3) Compile-time error!
result2 = light1 instanceof TubeLight; // (4) false: peer class.
// TubeLight tubeLight1 = (TubeLight) light1; // (5) ClassCastException!
result3 = light1 instanceof SpotLightBulb; // (6) false: superclass.
// SpotLightBulb spotRef = (SpotLightBulb) light1;// (7) ClassCastException!
light1 = new NeonLight(); // (8)
if (light1 instanceof TubeLight) { // (9) true.
TubeLight tubeLight2 = (TubeLight) light1; // (10) OK.
// Can now use tubeLight2 to access an object of the class NeonLight,
// but only those members that the object inherits or overrides
// from the superclass TubeLight.
}
SpotLightBulb light2 = new SpotLightBulb(); // (11)
result4 = light2 instanceof Light; // (12) true.
Light light = (Light) light2; // (13) OK. Redundant cast.
}
}
Leave a Reply