It lets callers depend on an abstraction while runtime behavior still comes from the concrete object.
Upcasting narrows what the caller can see through the reference, which is useful for flexible design. It does not erase the runtime type, so overridden methods still come from the real implementation.