Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do Collections.unmodifiableXXX methods violate LSP? [closed]

Liskov Substitution principle is one of the principles of SOLID. I have read this principle some number of times now and have tried to understand it.

Here is what I make out of it,

This principle is related to strong behavioral contract among the hierarchy of classes. The subtypes should be able to be replaced with supertype without violating the contract.

I have read some other articles too and I am a bit lost thinking about this question. Do Collections.unmodifiableXXX() methods not violate LSP?

An excerpt from the article linked above:

In other words, when using an object through its base class interface, the user knows only the preconditions and postconditions of the base class. Thus, derived objects must not expect such users to obey preconditions that are stronger then those required by the base class

Why I think so?

Before

class SomeClass{       public List<Integer> list(){            return new ArrayList<Integer>(); //this is dumb but works       } } 

After

class SomeClass{      public List<Integer> list(){            return Collections.unmodifiableList(new ArrayList<Integer>()); //change in implementation      } } 

I cannot change the implentation of SomeClass to return unmodifiable list in future. The compilation will work but if the client somehow tried to alter the List returned then it would fail at runtime.

Is this why Guava has created separate ImmutableXXX interfaces for collections?

Isn't this a direct violation of LSP or I have totally got it wrong?

like image 527
Narendra Pathai Avatar asked Feb 26 '14 19:02

Narendra Pathai


People also ask

Which of the following are violation of LSP?

LSP violations symptomsDerivates that override a method of the base class method to give it completely new behaviour. Derivates that override a method of the superclass by an empty method. Derivates that document that certain methods inherited from the superclass should not be called by clients.

Does LSP apply to interfaces?

Ad 1): Yes. However, it is hard to make interfaces enforce the contracts, and they really should. Ad 2): Programming against an interface is a trade-off. As you point out interfaces encourages violation of LSP for all interfaces with a non-trivial contract.

What does Liskov Substitution Principle specify?

What does Liskov substitution principle specify? Explanation: Liskov substitution principle states that Objects in a program should be replaceable with instances of their sub types without altering the correctness of that program.


1 Answers

LSP says that every subclass must obey the same contracts as the superclass. Wether or not this is the case for Collections.unmodifiableXXX() thus depends on how this contract reads.

The objects returned by Collections.unmodifiableXXX() throw an exception if one tries to call any modifying method upon them. For instance, if add() is called, an UnsupportedOperationException will be thrown.

What is the general contract of add()? According to the API documentation it is:

Ensures that this collection contains the specified element (optional operation). Returns true if this collection changed as a result of the call. (Returns false if this collection does not permit duplicates and already contains the specified element.)

If this was the full contract, then indeed the unmodifiable variant could not be used in all places where a collection can be used. However, the specification continues and also says that:

If a collection refuses to add a particular element for any reason other than that it already contains the element, it must throw an exception (rather than returning false). This preserves the invariant that a collection always contains the specified element after this call returns.

This explicitly allows an implementation to have code which does not add the argument of add to the collection but results in an exception. Of course this includes the obligation for the client of the collection that they take that (legal) possibility into account.

Thus behavioural subtyping (or the LSP) is still fulfilled. But this shows that if one plans to have different behaviours in subclasses that must also be foreseen in the specification of the toplevel class.

Good question, by the way.

like image 160
Matt Avatar answered Sep 24 '22 16:09

Matt