Liskov Substitution Principle with Java Code Examples

0
This article describes what is Liskov Substitution Principle along with code examples in Java. Please feel free to comment/suggest if I missed to mention one or more important points. Also, sorry for the typos.

Following are the key points described later in this article:

  • What is Liskov Substitution Principle (LSP)?
  • Code Samples to Illustrate LSP
  • What code quality characteristic is represented by LSP?

 

What is Liskov Substitution Principle (LSP)?

Take a look at this paper on Liskov Substitution Principle which provides great details on Liskov Substitution principle. As per LSP, functions that use references to the base classes must be able to use objects of derived class without knowing it. In simple words, derived classes must be substitutable for the base class. To illustrate LSP, let’s take an example of Rectangle and Square. One tends to establish the ISA relationship, thus, calling Square as a rectangle (Square is a Rectangle). However, there arises a problem (hence, violation of LSP) which shall be demonstrated with the code sample. Take a look at code illustration below to understand LSP in detail.

 

Java Code Samples to Illustrate LSP

Liskov Substitution principle is popularly explained using Square and Rectangle example. Let’s assume we try to establish ISA relationship between Square and Rectangle. Thus, we call “Square is a Rectangle”. Below code represents the relationship.

/**
 * Rectangle 
 * @author Ajitesh Shukla
 */
public class Rectangle {
	
	private int length;
	private int breadth;
	
	public int getLength() {
		return length;
	}
	public void setLength(int length) {
		this.length = length;
	}
	public int getBreadth() {
		return breadth;
	}
	public void setBreadth(int breadth) {
		this.breadth = breadth;
	}	
	public int getArea() {
		return this.length * this.breadth;
	}
}

Below is the code for Square. Note that Square extends Rectangle.

/**
 * Square class; Square inherits from Rectangle; 
 * Represents ISA relationship - Square is a Rectangle
 * @author Ajitesh Shukla
 */
public class Square extends Rectangle {
	
	@Override
	public void setBreadth(int breadth) {
		super.setBreadth(breadth);
		super.setLength(breadth);
	}
	@Override
	public void setLength(int length) {
		super.setLength(length);
		super.setBreadth(length);
	}
}
The code below represents the function which has Rectangle as an argument. As per the principle, the functions that use references to the base classes must be able to use objects of derived class without knowing it. Thus, in the example shown below, the function calculateArea which uses the reference of “Rectangle” should be able to use the objects of derived class such as Square and fulfill the requirement posed by Rectangle definition.
Look at the calculateArea method in the code below. One should note that as per the definition of Rectangle, following must always hold true given the data below:

  • Length must always be equal to the length passed as the input to method, setLength
  • Breadth must always be equal to the breadth passed as input to method, setBreadth
  • Area must always be equal to product of length and breadth

In case, we try to establish ISA relationship between Square and Rectangle such that we call “Square is a Rectangle”, below code would start behaving unexpectedly if an instance of Square is passed. Assertion error will be thrown in case of check for area and check for breadth, although the program will terminate as the assertion error is thrown due to failure of Area check.

/**
 * The class demonstrates the Liskov Substitution Principle (LSP)
 * 
 * As per the principle, the functions that use references to the base classes must be able to use objects of derived class without knowing it.
 * Thus, in the example shown below, the function calculateArea which uses the reference of "Rectangle" should be able to use the objects of 
 * derived class such as Square and fulfill the requirement posed by Rectangle definition.
 *  
 * @author Ajitesh Shukla
 */
public class LSPDemo {
	/**
	 * One should note that as per the definition of Rectangle, following must always hold true given the data below:
	 * 1. Length must always be equal to the length passed as the input to method, setLength
	 * 2. Breadth must always be equal to the breadth passed as input to method, setBreadth
	 * 3. Area must always be equal to product of length and breadth
	 * 
	 * In case, we try to establish ISA relationship between Square and Rectangle such that we call "Square is a Rectangle", 
	 * below code would start behaving unexpectedly if an instance of Square is passed
	 * Assertion error will be thrown in case of check for area and check for breadth, although the program will terminate as
	 * the assertion error is thrown due to failure of Area check.
	 *   
	 * @param r Instance of Rectangle
	 */
	public void calculateArea(Rectangle r) {
		r.setBreadth(2);
		r.setLength(3);
		//
		// Assert Area
		// 
		// From the code, the expected behavior is that 
		// the area of the rectangle is equal to 6
		//
		assert r.getArea() == 6 : printError("area", r);
		//
		// Assert Length & Breadth
		//
		// From the code, the expected behavior is that 
		// the length should always be equal to 3 and
		// the breadth should always be equal to 2
		//
		assert r.getLength() == 3 : printError("length", r);
		assert r.getBreadth() == 2 : printError("breadth", r);
	}
	
	private String printError(String errorIdentifer, Rectangle r) {
		return "Unexpected value of " + errorIdentifer + "  for instance of " + r.getClass().getName();
	}
	
	public static void main(String[] args) {
		LSPDemo lsp = new LSPDemo();
		//
		// An instance of Rectangle is passed
		//
		lsp.calculateArea(new Rectangle());
		//
		// An instance of Square is passed
		//
		lsp.calculateArea(new Square());
	}
}

Given the above code, what is the problem with Square-Rectangle ISA relationship?

  • The Square class does not need method such as setBreadth or setLength as the sides of a square are equal. This is wasteful. Imagine of hundreds of thousands of Square objects.
  • LSPDemo class would need to know the details of derived classes of rectangle (such as Square) to code appropriately to avoid throwing error. The change in existing code to take care of derived class breaks the open-closed principle in first place.

 

What code quality characteristic is represented by LSP?

Following are some of the code quality characteristic which is represented by Liskov Substitution Principle.

  • It is only when derived types are completely substitutable for their base types that functions which use those base types can be reused with impunity, and the derived types can be changed with impunity.
  • LSP is also, at times, termed as “Design by Contract”. Using this scheme, methods of classes declare pre-conditions and post-conditions. The preconditions must be true in order for the method to execute. Upon completion, the method guarantees that the postcondition will be true.
  • Design by Contract does influence the declaration of “throws” exceptions as well as the throwing of runtime exceptions and try/catch in general.

 

Ajitesh Kumar

Ajitesh Kumar

Ajitesh is passionate about various different technologies including programming languages such as Java/JEE, Javascript, PHP, .NET, C/C++, mobile programming languages etc and, computing fundamentals such as application security, cloud computing, API, mobile apps, google glass, big data etc.Recently, he has been digging deep into the field of data science and machine learning.

Follow him on Twitter and Google+.
Ajitesh Kumar

Leave A Reply