Liskov principle: violation by type-hinting

Posted by Elias Van Ootegem on Programmers See other posts from Programmers or by Elias Van Ootegem
Published on 2013-11-12T09:51:48Z Indexed on 2013/11/12 10:20 UTC
Read the original article Hit count: 288

Filed under:
|
|

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?

© Programmers or respective owner

Related posts about php

Related posts about object-oriented