(Sub) b this makes the compiler happy because the compile type of "b" (Base) is a supertype of Sub. However because it is a downcast the "interpreter" must make another check at runtime. It must ensure that the object pointed by "b" is really an object of type "Sub" (or one of its subtypes)
If the last check were not carried out, an object of type Base could be referred by a variable of type Sub; this could lead to such object to receive a method call declared in type Sub but not in Base. That is an object could receive a message for which is not prepared for.
Java is a type safe language. You cannot call methods on objects that do not correspond with its type.
The exact rules for casting are given in
JLS 5.5 Casting Conversion As a
general guide, keep in mind that the compiler will complain if there is no hierarquical relationship between the compile type of the expression to be casted "b", and the type inside the cast operator "()" (*). Later at runtime, a check of the real type of the object pointed by the expression maybe needed.
(*) Say there there is no a father-son relationship but they are siblings.