Reference values like primitive value can be cast and mostly casted during assignment, passing as parameter or via explicit casting. Widening conversions are permited in primitive data types but narrowing conversion must be done explicitly. In similar way, upcasting ( subtypes to its super type) is allowed and downcasting (supertype to subtype) can be done explicitly.
upcasting and downcasting example
Why does the statement Card c = new BirthdayCard(); is allowed and BirthdayCard cardBirthday = c; is not allowed?
It’s because BirthdayCard is always an instance of Card but it is not necessarily an instance of BirthdayCard. Compiler is not enough intelligent to determine that c is a BirthdayCard object. To achieve this use explicit casting BirthdayCard cardBirthday = (BirthdayCard) c;