According to the Liskov principle, a construction like the one below is invalid, as it strengthens a pre-condition.
I know the example is pointless/nonsense, but when I last asked a question like this, and used a more elaborate code sample, it seemed to distract people too much from the actual question.
//Data models
abstract class Argument
{
protected $value = null;
public function getValue()
{
return $this->value;
}
abstract public function setValue($val);
}
class Numeric extends Argument
{
public function setValue($val)
{
$this->value = $val + 0;//coerce to number
return $this;
}
}
//used here:
abstract class Output
{
public function printValue(Argument $arg)
{
echo $this->format($arg);
return $this;
}
abstract public function format(Argument $arg);
}
class OutputNumeric extends Output
{
public function format(Numeric $arg)//<-- VIOLATION!
{
$format = is_float($arg->getValue()) ? '%.3f' : '%d';
return sprintf($format, $arg->getValue());
}
}
My question is this:
Why would this kind of "violation" be considered harmful? So much so that some languages, like the one I used in this example (PHP), don't even allow this?
I'm not allowed to strengthen the type-hint of an abstract method but, by overriding the printValue method, I am allowed to write:
class OutputNumeric extends Output
{
final public function printValue(Numeric $arg)
{
echo $this->format($arg);
}
public function format(Argument $arg)
{
$format = is_float($arg->getValue()) ? '%.3f' : '%d';
return sprintf($format, $arg->getValue());
}
}
But this would imply repeating myself for each and every child of Output, and makes my objects harder to reuse.
I understand why the Liskov principle exists, don't get me wrong, but I find it somewhat difficult to fathom why the signature of an abstract method in an abstract class has to be adhered to so much stricter than a non-abstract method.
Could someone explain to me why I'm not allowed to hind at a child class, in a child class?
The way I see it, the child class OutputNumeric is a specific use-case of Output, and thus might need a specific instance of Argument, namely Numeric. Is it really so wrong of me to write code like this?