You can use the subtypes.
If S is a subtype of T, then you may replace objects of type T with objects of type S without breaking anything. Let’s say your type T is a Chevy, and the subtypes of Chevy are Corvette, Camaro, and Silverado. Then anywhere you can use a Chevy, you should be able to use a Corvette or a Camaro. Or even a Silverado. They’re all subtypes of Chevy.
Okay, so maybe that all seems like common sense, and perhaps we should call in Captain Obvious. Here’s the problem: adding or changing setter methods in the subtype can cause the subtype to behave differently.
For example, take the type Rectangle. To create a Rectangle, you will need to know its length and width. Fine. Now examine a subtype of Rectangle: the Square. The square behaves differently. You can create a square just by knowing its width; you don’t need to specify its length. Also, the formulas for area and perimeter and simpler for squares than for rectangles.
It comes down to contracts. When you created the Rectangle class, you expected its subclasses also to have width and length. Squares behave differently and have to know that their width and length are the same. With a Rectangle object, one expects to be able to set the width to 2 and the length to 3 and get an area of 6. But with a Square, if you set the width to 2, the object sets the length to 2 as well. Then, if you set the square’s length to 3, the object will set the width to 3. The area comes out as 9, not matching the 6 you would expect when setting the width to 2 and the length to 3.
If you write your subtypes in such a way that they obey all of the contracts of the parent object, then you are on your way to getting the Liskov substitution principle. Mind your interfaces.