- Upcasts can be checked at compile time – the type system guarantees that the cast succeeds.
- Downcasts cannot (in general) be checked at compile time, so they are always checked at runtime.
- Unrelated types cannot be cast to each other.
The compiler considers only the static types. The runtime checks the dynamic (runtime) type.
Looking at your examples:
Other x = new Other();
Derived d = (Derived)x;
The static type of x
is Other
. This is unrelated to Derived
so the cast fails at compile time.
Base x = new Base();
Derived d = (Derived)x;
The static type of x
is now Base
. Something of type Base
might have dynamic type Derived
, so this is a downcast. In general the compiler can’t know from the static type of x
if it the runtime type is Base
, Derived
, of some other subclass of Base
. So the decision of whether the cast is allowed is left to the runtime.