Disclaimer: Super (over)simplistic intuitive explanation, without getting into proper technical detail.
I will start by pointing out that downcasting may be possible in special cases; specifically, on a "parent" object that was itself created by upcasting a "child" object. Hopefully the reason for this will be clear in a sec.
Say you have a Parent
object with field x
.
And a Child
object which extends this with another field y
.
When you instantiate a Parent
object normally, java allocates enough memory to hold x
.
When you instantiate a Child
object, java allocates enough memory to hold both x
and y
.
When you upcast a Child
object onto a Parent
one, the memory layout doesn't change; java simply ignores the memory bit that was reserved for y
, and treates all calls to this object as if it only contained x
.
But if you were to attempt to downcast a Parent
object into a Child
one, you would essentially be missing the bit that refers to y
in the object's memory footprint. Since the intent is one of 'casting' rather than "copying the object somewhere else", casting in this case is not allowed.
In the case the the Parent
object was originally obtained by upcasting a Child
object, it is then possible to downcast it back into a Child
object; java treats this as a special case, since the memory layout would allow it.
This is also the reason that downcasting is treated as a runtime bug, rather than a compilation one.
PS. Note that the corollary from this is that when one upcasts at instantiation, while this is treated as a Parent
object in the code, an entire Child
object is stored in memory -- which is why a successful downcast back to a Child
object can reoccur at any time.