Because their (non-polymorphic methods) may not be
... and therefore it is not safe (with type safety)
By assigning a value to a variable of a base type for it (i.e. an up-cast , which generalizes caste, conversion to a supertype / base type), you limit your ability to work with it only on what is in the base type . At the language level, you lose information about what type the source value was (at runtime it still has, but more on that later), you have a base type value. What is really inside of it, you should not be interested in it.
Instead, you get the opportunity to work with the value of any suitable type, even one that you may not even be aware of at the time of development. For example, for your library, the user can implement their own class and pass its objects to your code. Without your participation in its product.
This applies not only to classes and their base (by inheritance) classes, but also to classes and the interfaces they implement . Interfaces are also types, although they do not exist at all in their pure form, but they determine the common features of all types suitable for them: therefore, it is safe to use the value of an interface type only with these very common features .
It makes sense if you remember what errors the typing is checking for the compiler to avoid: basically these are errors of getting a value in some place that does not have some of the capabilities required there. Capability determines the type of variable . The compiler tries to prove that these possibilities exist, and if it cannot, it tells you how to place a potential error.
Specifically, in Java there is a down-casting, a conversion from a more general type to a more specific one, but only explicit , which already hints that this action is dangerous and can end in an error ( ClassCastException ).
But this is not everywhere and they should be used only in extreme cases.