• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Paul Clapham
  • Ron McLeod
  • Bear Bibeault
  • Liutauras Vilda
Sheriffs:
  • Jeanne Boyarsky
  • Junilu Lacar
  • Henry Wong
Saloon Keepers:
  • Tim Moores
  • Stephan van Hulst
  • Jj Roberts
  • Tim Holloway
  • Piet Souris
Bartenders:
  • Himai Minh
  • Carey Brown
  • salvin francis

Trying to understand the Liskov principle

 
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Liskov principle says that "Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program". But doesnt this happen all time time? e.g a Dog is always an animal. A ford is always a car.  Thanks
 
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Monica Shiralkar wrote:But doesnt this happen all time time? e.g a Dog is always an animal. A ford is always a car


That's not what the principle is about.

Using your examples, any code that is programmed to an Animal should work regardless of whether a Dog, Cat, Cow, or whatever other subclass of Animal that can be given as an implementation. Likewise, any code that references a Car should work whether it's given a Ford, Toyota, Porsche, Saab, or Tesla.

Here's a simple violation of the principle:

Clearly, this code will not work with anything other than a Dog. You might think this isn't something that would happen in the real world but I've seen code like this running in production.
 
Monica Shiralkar
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:

.



I am still confused about Liskov Principle.If the above code is a violation of Liskov Principe ,then what is the code for this which is not a violation of this principle for a similar example .?
 
Marshal
Posts: 71047
291
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You try writing a version of that method that will take any kind of Animal. Say what assumptions you are making.
 
Monica Shiralkar
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:You try writing a version of that method that will take any kind of Animal. Say what assumptions you are making.



Sure.What does this means "method that will take any kind of Animal".I mean take argument of type Animal and do what after that ?
 
Campbell Ritchie
Marshal
Posts: 71047
291
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Monica Shiralkar wrote:. . . do what after that ?

What do you think? Take it for a walk.

Everybody knows that octopuses are more intelligent than dogs, but I have never seen anybody taking their pet octopus for a walk round the park. But octopuses can walk; I have seen one on YouTube which escaped its cage tank at an aquarium at Plymouth and managed to walk to the harbour, where it presumably found more water.
 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The problem is that the method signature declares an Animal parameter. The code inside, however, assumes a specific implementation of Animal. This is a violation of LSP because it will not work with any Animal, only a Dog. To fix it, you would need to eliminate any assumption of what kind of Animal it is and rely solely on the code working based on the definition given by Animal.
 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
There are other ways LSP can be violated as well and most of them have to do with how subclasses are implemented. For example, a subclass that throws a NotImplementedException for a method that has been implemented in the superclass violates LSP. If the superclass accepts one kind of input, its subclasses should also accept the same input. Otherwise, this could compromise the robustness and correctness of the program.
 
Monica Shiralkar
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:

Monica Shiralkar wrote:. . . do what after that ?

What do you think? Take it for a walk.

Everybody knows that octopuses are more intelligent than dogs, but I have never seen anybody taking their pet octopus for a walk round the park. But octopuses can walk; I have seen one on YouTube which escaped its cage tank at an aquarium at Plymouth and managed to walk to the harbour, where it presumably found more water.



I came up with the below code but not sure how to create 1) code as per lislov using this example and 2) code which is violating of lislov using this example .

 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
It's hard to critique that code because there appears to be no point to it. For example, why would you have an exercise() method that takes an Animal as its parameter and then have code in there that does nothing related to the Animal passed in? What's the point? If you want to understand the principle, use reasonable examples.
 
Campbell Ritchie
Marshal
Posts: 71047
291
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:. . . . If the superclass accepts one kind of input, its subclasses should also accept the same input. . . . .

In OO that would translate as

Junilu Lacar wrote:. . . . If the overridden method accepts one kind of input, itsoverriding methods should also accept the same input. . . . .

. . . or more input. As far as I am concerned, if the overriding method would throw an exception for input which the overridden method would accept, that is incorrect overriding. It is permissible for the overriding method to accept more inputs than the overridden method. There is a description in Object‑Oriented Software Construction by Bertrand Meyer, but I think he doesn't say, ”Liskov.” Can't fid the page in my copy; sorry.
You get questions in cert exams about whether an overriding (or implementing) method will or won't compile:-That code might compile because the compiler doesn't notice the unchecked exception, but I still think that is incorrectly implemented because the specification doesn't say anything about rejecting nulls.
 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This would be a more reasonable example:

You should expect the code in exercise() to work regardless of what kind of Animal subclass is passed to it. If you create some subclass of Animal that causes exercise() to fail when passed an instance of that subclass while others succeed, then you have violated LSP.
 
Monica Shiralkar
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:This would be a more reasonable example:

.




Thanks understood the success case.


If you create some subclass of Animal that causes exercise() to fail when passed an instance of that subclass while others succeed



For the case which is violation of liskov principle , when would this happen ? I mean as long as the class implements Animal , one can successfully invoke walk method on its object.So when will it fail ?
 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
It will fail if you pass in an implementation of Animal that would cause line 10 to fail. For example, if you had this:

This is a contrived example but the idea is that if other implementations succeed and this particular implementations fails when walk() is called, then you've violated LSP.
 
Monica Shiralkar
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In this the walk method is being overridden and the overridden method throws an exception.

Is it also possible to violate this principle in this example by using a case where no exception is thrown ?

I am still quite confused regarding liskov principle .
 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You have to be more specific about your confusion. It's not that complicated really. What part of "code that uses references to a superclass should still work correctly when operating on instances of a subclass" don't you understand?
 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Monica Shiralkar wrote:Is it also possible to violate this principle in this example by using a case where no exception is thrown?



Sure. Let's say an Animal defines "walking" to be any movement that is done at a rate of say 20 to 40 paces per minute and "running" to be any movement done at a rate that is greater than 40 and less than 120 paces per minute. If an implementation of Animal implements its walk method that is outside of the specified range of 20-40 paces per minute and instead does 90 paces per minute, then it will have violated the behavior defined by Animal.walk() -- any code that relies on an Animal to stay within the 20-40 paces per minute range will then not behave as expected if given this particular instance of Animal subclass. So you have violated LSP here.
 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Liskov is related to design by contract. If a superclass defines behavior and you have code that relies on that defined behavior, then any and all subclasses should conform to that definition of behavior. Any departure from that defined behavior by a subclass is a violation of LSP.
 
Marshal
Posts: 26128
77
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:Sure. Let's say an Animal defines "walking" to be any movement that is done at a rate of say 20 to 40 paces per minute and "running" to be any movement done at a rate that is greater than 40 and less than 120 paces per minute.



And then let's say you create a Snail subclass. First of all it's unclear what a "pace" is for a snail, but anyway it isn't likely to perform either "walking" or "running" according to that definition. So Snail breaks the Liskov principle.
 
Monica Shiralkar
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote: What part of "code that uses references to a superclass should still work correctly when operating on instances of a subclass" don't you understand?



What I was unable to understand was the reverse of this,that is the violation.
 
Monica Shiralkar
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
As per Liskov principle ,objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.

As per my understanding this will always happen unless the subclass overrides that method and either changes that implementation or throws some exception.Is that correct ?

Is yes, then but any subclass in Java has the right to override and change the implementation.

So, I am confused that how can the 2 things above be true at the same time.
 
Campbell Ritchie
Marshal
Posts: 71047
291
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Monica Shiralkar wrote:. . . any subclass in Java has the right to override and change the implementation. . . . .

Any subclass must always obey the principle that a subclass IS‑A superclass. An overriding method must always obey the specification given by the overridden method. Any changes must follow those constraints.Both those methods calculate pay (without calculating taxes) and may or may not throw such an exception. It would be wrong to implement that abstract method to calculate taxes. It would be all right to pass 1000000 hours to one overriding version because it ignores that parameter anyway. It is necessary to explain what the method does (=documentation comments) in order to allow it to be overridden. I shall let you guess what the last change I made to the documentation comments was, and how that causes both implementations to break the LSP. Also tell me what I put in the documentation comments that restrains the code in the future.
 
Monica Shiralkar
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Does that mean that everytime overriding takes place (by the sub class ), Liskov principle gets violated.

If not ,then what would be a case where subclass does overriding but does not violate Liskov's Principe.
 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
There are several examples already given in this thread. Why don't you come up with your own example and share it so we can tell you if you understand the concept.
 
Saloon Keeper
Posts: 12488
269
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Monica Shiralkar wrote:Does that mean that everytime overriding takes place (by the sub class ), Liskov principle gets violated.


No.

The Liskov substitution principle is formally defined as:

Let ϕ(x) be a property provable about objects x of type T. Then ϕ(y) should be true for objects y of type S where S is a subtype of T.


You should take this to mean that if a method or class contract guarantees something about the class or method it documents, then that guarantee must also be made by subclasses.

For instance, the documentation of the Set interface says that collections that implement the Set interface may not contain two equal elements. The Liskov substitution principle then says that there is no subclass of Set in which you CAN have two equals elements.
 
Monica Shiralkar
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I have understood what Liskov principle is saying.
I also understand what is overriding.

I am trying to understand how both can happen at same time.

Liskov principle says if a method contract guarantees something , then that guarantee must also be made by the overriding method in subclass.

Overriding says that subclass can give different implementation by using the same signature.

Example:

Super class Animal has method makeSound.

Now, subclass Pig can give it new implementation and display Grunt sound.
Similarily, dog can also give new implementation and display Bark sound.
The subclass method (makeSound) has the right to do so in this example.So what right does the subclass method not have in this example ?
 
Stephan van Hulst
Saloon Keeper
Posts: 12488
269
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This isn't about rights. It's about guarantees.

What guarantee does the Animal class make? Pig and Dog must make the same guarantee.

For instance, the Animal class could guarantee that the makeSound() method never returns null. You then may not substitute one Animal implementation by another that implements makeSound() by returning null.
 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You have to look at it from the context of the calling code that's using a reference to the superclass. That code is what determines what is "right" vs. "wrong" in a subclass with respect to Liskov.

In your example, if the contract defined by the makeSound() method in the superclass states that it will display a String then all subclasses must display a String if they override makeSound(). If there is a subclass that does not display a String but say only plays a sound file instead, then it has broken the contract. In that case, LSP will have been violated.
 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Here's a more realistic example. The Object class defines a contract for equals() and hashCode() that must be followed by all other classes. Classes that override equals() will have different implementations that are specific to the class' own characteristics but the general behavior as defined by Object.equals() must still be honored, otherwise you will have violated LSP and this could lead to bugs in your code.
 
Monica Shiralkar
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks all.

All these years, I was thinking that during overriding one can give any new implementation just that we have to use the same signature. Now I have come to know that during overriding , one has to be careful that one does not break the contract of the original method.  

I wonder why this does not get mentioned in the documentation of Overriding that you can give any new implementation but without breaking Liskov principle.

While implementing, where exactly can one check the contract of the original method?
 
Stephan van Hulst
Saloon Keeper
Posts: 12488
269
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Monica Shiralkar wrote:I wonder why this does not get mentioned in the documentation of Overriding that you can give any new implementation but without breaking Liskov principle.


Because it is considered a widely known principle of object oriented programming.

While implementing, where exactly can one check the contract of the original method?


In the documentation of the original class and method. Check the JavaDocs.
 
Monica Shiralkar
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
so in the comments over the method and class.
 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Monica Shiralkar wrote:I wonder why this does not get mentioned in the documentation of Overriding that you can give any new implementation but without breaking Liskov principle.


What documentation of Overriding are you referring to?

While implementing, where exactly can one check the contract of the original method?


That depends. Standard library classes usually have good JavaDocs. Otherwise, there are all kinds of documentation you could track down, it just depends on how diligent the developers are in writing that kind of thing down. Personally, I would prefer to rely on unit tests that I can study and run. The last resort if there really is no documentation or existing unit tests would be to write characterization tests to document your understand of how the class currently behaves.
 
Campbell Ritchie
Marshal
Posts: 71047
291
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Stephan van Hulst wrote:. . . if a method or class contract guarantees something . . . then that guarantee must also be made by subclasses. . . .

Wouldn't you also say that the classes involved must honour that guarantee?
 
Stephan van Hulst
Saloon Keeper
Posts: 12488
269
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Monica Shiralkar wrote:so in the comments over the method and class.


Yes, but don't call them "comments". Use the word "comments" to refer to inline comments (those starting with //) and block comments (those starting with /*). Refer to the blocks that start with /** as "documentation" or "contract".
 
Stephan van Hulst
Saloon Keeper
Posts: 12488
269
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Wouldn't you also say that the classes involved must honour that guarantee?


If you mean calling classes/methods, then no. Callers are not responsible for the correctness of the callees. If a callee only makes guarantees under certain circumstances, any bugs that arise from incorrect usage of the callee will automatically become the problem of the caller.

A good example of this is the TreeSet class. TreeSet only guarantees correct Set behavior when the compareTo() method of its elements is consistent with equals(). A class that uses TreeSet with an element type such as BigDecimal does so at its own peril, but strictly speaking it MUST not honor the guarantee.
 
Monica Shiralkar
Ranch Foreman
Posts: 2060
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:

In your example, if the contract defined by the makeSound() method in the superclass states that it will display a String then all subclasses must display a String if they override makeSound(). If there is a subclass that does not display a String but say only plays a sound file instead, then it has broken the contract. In that case, LSP will have been violated.





I understand that in the above example, as per Liskov principle we should not do
But will it give me error? What problem exactly will I encounter during runtime at line

(Ofcourse after the implementation of the TODO in the line for  )
 
Campbell Ritchie
Marshal
Posts: 71047
291
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Stephan van Hulst wrote:. . . If you mean calling classes/methods, then no. . . . .

No, that wasn't what I meant; I was thinking about callees only.

[additional:]Unless you mean, “if you pass the arguments in the correct form,” constitutes part of the guarantee.
 
Junilu Lacar
Sheriff
Posts: 15995
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Monica Shiralkar wrote:
But will it give me error? What problem exactly will I encounter during runtime at line


Whether or not it will cause an error at runtime is not the issue. It's about conforming to the contract. If the contract for makeSound() was to display some text and the tiger instance doesn't do it but instead plays a sound file, you still have behavior that is inconsistent with the expectation of displaying text. The program doesn't have to blow up as a result of violating the principle. All it has to do is behave inconsistently or unexpectedly. If you want to change the expectation to say "makeSound() will either display text that is representative of the sound the animal would make or produce a sound on the speaker" then you are no longer in violation of the principle and you didn't even have to change the code! Again, it's about the contract and expected behavior.
 
reply
    Bookmark Topic Watch Topic
  • New Topic