The term for this is subtyping, or substitution.
When two types have an inheritance relationship, one is said to be a subtype and the other is the supertype. When this is applicable, a subtype instance can be used in a context where a supertype instance was expected (the context being a local variable, a field, a parameter, etc).
This is why you may often hear that inheritance denotes an is-a relationship. Compare with composition (the practice of including an object as a member of another object), which denotes a has-a relationship (and is the other way of achieving code reuse when dealing with classes).
Back to inheritance, if your classes are defined like this, for example:
class Banana : Fruit { ... }
We'd say that Banana
(apart from being a Banana
, obviously) is a Fruit
as well. Because of that relationship, you can do this, obviously:
Banana obj = new Banana();
But you do this as well:
Fruit obj = new Banana();
And you can do something similar whenever a field or a method parameter expects a Fruit
.
This type of relationship is useful when you want parts of your code to use an object, but you don't want them to know too many details about that object (maybe because those details are irrelevant or subject to change). If those parts can do their job with less specific information (in this case, the fact that the object is some kind of Fruit
and not specifically a Banana
), then it's preferable to have them work with that. This is a form of decoupling and it's a generally desired property of code as a project gets more and more complex.