LSP forbids to violate the contracts of a supertype in a subtype, it does not forbid to change the behaviour of any method (within the bounds of that contract).
For example, lets assume you have a superclass Report
, associated with a certain object Foo
, with a method ToString()
, and subtypes HTMLReport
, XMLReport
and TextReport
. Lets further assume Report
is not abstract and the default implementation of ToString()
is to return the empty string. Now you define a contract in the following manner:
ToString()
shall deliver a string with a textual representation of Foo
in a certain text format (and the empty string if the format is not defined so far).
ToString()
shall not mutate the Report object
ToString()
shall never throw an Exception
So the subclasses can easily override the ToString
method, each one implementing a different behaviour, but all perfectly following the LSP.
On the other hand, if your contract would be
ToString()
shall always return the empty string
then overriding it and return something different would violate the LSP - but such a contract would obviously make not much sense for any real world scenario.