I have read a few questions on the SO and elsewhere and still do not understand well where this "widening" of a parameter type can be helpful, i.e. compliying to Liskov substitution principle. The following code I took from an answer on the SO, explaining contravariance:
//Contravariance of parameter types: OK class Food class FastFood extends Food class Person { eat(FastFood food) } class FatPerson extends Person { eat(Food food) } So I understand that the overridden method accepts more generic paramater than the method in its ancestor. But in practice, how does this help? I mean, if the original method works with certain properties of the derived type, none of this will be available in the derivative using the supertype of the parameter. Therefore, I might have issues with fulfilling the contract postconditions, if those relate to the subtype somehow. Like:
class Animal {} class Cat { void Meow() void CatSpecificThing()} ... class A { List<Cat> ListOfCats; void X(Cat c) { c.Meow() c.CatSpecificThings() ListOfCats.Add(c) } } class B : A { void X(Animal a) { //how is this now useful? I cannot do anything that needed Cat } } Let's say the postcondition of X method is to update the ListOfCats. But in the overriden method in the derived class, I would not be able to do it if there was just the supertype..?
I would be extremely happy for a simple example that demonstrates how this is useful.
Bis an odd duck. It is anA, and thus has a list of cats. Presumably that list of cats is used somehow, which is to say that it is returned by some method. If the same type is both consumed as a parameter and returned as a return type it pretty much has to be invariant rather than contravariant (as we'd like for the parameter) and covariant (as we'd like for a return type). As it stands, yourBwould have to have its own list of animals, usingA'sXwith only cats...