Если вы возьмете родительский класс и расширите его, у класса будут все функции, которые есть у родительского класса, плюс еще немного.
Если вы присваиваете объект дочернего типа объекту родительского типа, например:
Parent aParent = aChild;
Вы сводите интерфейс дочернего объекта к функциям базового класса. Это совершенно нормально, потому что это означает, что некоторые новые функции ребенка не используются в этом контексте.
Если вы сделаете это наоборот и попытаетесь привести базовый класс к ребенку, вы получите объект, который может оправдать ожидания в его интерфейсе.
Например, вы определяете базовый класс, например:
Child extends Parent
void doSomeSpecialChildStuff...
Теперь вы создаете Parent и присваиваете его дочернему объекту.
Parent aParent = new Child()
Ваш язык программирования теперь считает объект aParent дочерним. Проблема в том, что теперь это было бы совершенно справедливо для этого:
aParent.doSomeSpecialChildStuff()
Теперь вы вызываете метод, который не определен для объекта, но интерфейс объекта говорит, что он определен.