Why cast after an instanceOf?

In the example below (from my coursepack), we want to give to the Square instance c1 the reference of some other object p1, but only if those 2 are of compatible types.

if (p1 instanceof Square) {c1 = (Square) p1;}

What I don't understand here is that we first check that p1 is indeed a Square, and then we still cast it. If it's a Square, why cast?

I suspect the answer lies in the distinction between apparent and actual types, but I'm confused nonetheless...

Edit:
How would the compiler deal with:

if (p1 instanceof Square) {c1 = p1;}

Edit2:
Is the issue that instanceof checks for the actual type rather than the apparent type? And then that the cast changes the apparent type?


Keep in mind, you could always assign an instance of Square to a type higher up the inheritance chain. You may then want to cast the less specific type to the more specific type, in which case you need to be sure that your cast is valid:

Object p1 = new Square();
Square c1;

if(p1 instanceof Square)
    c1 = (Square) p1;

The compiler does not infer that since you are in the block, you have done a successful check for the type of the object. An explicit cast is still required to tell the compiler that you wish to reference the object as a different type.

if (p1 instanceof Square) {
    // if we are in here, we (programmer) know it's an instance of Square
    // Here, we explicitly tell the compiler that p1 is a Square
    c1 = (Square) p1;
}

In C# you can do the check and the cast in 1 call:

c1 = p1 as Square;

This will cast p1 to a Square, and if the cast fails, c1 will be set to null.


Old code will not work correctly

The implied cast feature is justified after all but we have trouble to implement this FR to java because of backward-compatibility.

See this:

public class A {
    public static void draw(Square s){...} // with implied cast
    public static void draw(Object o){...} // without implied cast
    public static void main(String[] args) {
        final Object foo = new Square();
        if (foo instanceof Square) {
            draw(foo);
        }
    }
}

The current JDK would compile the usage of the second declared method. If we implement this FR in java, it would compile to use the first method!

🔴 JDK 14

We finally implemented this feature in JDK 14. As you might have noticed you can declare a new variable within the instanceof-linkage. This new variable has been defined by the value of a automatically upcast to the specified type.

if (any instanceof String s) {
  System.out.println(s);
}

There's a difference between measuring if some object will fit in a box, and actually putting it in the box. instanceof is the former, and casting is the latter.