0

I have just come across Liskov Substitution Principle and casting during a lecture of (Java) Object-Oriented Programming. I understand what the principle states, that is, I can initialise a subclass with the type of a superclass:

SuperClass superClass = new SubClass();

My first concern regards the purpose of such operation. Why can't I just declare the subclass as usual (example follows)?

SubClass subClass = new SubClass();

Right after this, I got stuck on casting, as follows:

SuperClass superClass = new SubClass();
SubClass subClass = (SubClass)superClass;

Again, I struggle understanding the point of all of this.

Can anyone provide any clarification on the purpose of these procedures?

2 Answers2

1

Liskov Substitution Principle means that subclasses should be done with the understanding that you should only really care what subclass an instance is when it gets instantiated. From then onwards, it should be treated exactly like the superclass with no added methods or special treatment.

The biggest advantage to this approach is that should you decide that you want to replace one subclass with another, you could! It would be called in precisely the same way as the previous subclass were called. If this isn't convenient for your new subclass, then Liskov would say that means it shouldn't truly be a subclass of that superclass. Subclasses should be treated indiscriminately.

Regarding your casting, when you assign SubClass to SuperClass, you now have an instance you can use throughout your entire program without caring that it is SubClass specifically. The following line where you try to cast superClass instance back into a SubClass can be done, but you're probably not doing something correctly (again, because you shouldn't care what subclass it is).

Generally you'll see a factory or a method instantiate it as follows:

public SuperClass getSuperClass() {
    SubClass subClass = new SubClass();

    // Here is the only place where you should perform subclass specific
    // calls.  The less you need to do here, the better.
    subClass.setSubClassProperty(this);

    return subClass;
}

Note the implicit cast to SuperClass as I return a SubClass instance and the caller is given a SuperClass instance. This is how we prefer it. The caller could receive a SubClass instance instead, but we wouldn't want the temptation for the caller to use SubClass methods.

Neil
  • 5,762
  • 24
  • 36
0

Because this way you can use superclass methods without knowing anything about subclass. Consider next example:

Imagine things which can be drawn:

public abstract class Thing {
   public abstract void draw();
}

if you have a list of such things you can draw them in a loop:

List<Thing> list = ...
for(thing: list) {
   thing.draw()
}

Now you want to draw actual things: circles, squares, etc. So you write concrete classes:

 public class Circle extends Thing {
      private final Point center;

      public Circle(Point center) {
           this.center = center; 
      }     

      public void draw() {
             //draw a circle in coordinates center
      }
 }

 public class Square...

How can you create a list of concrete things? Easily due to that superclass to subclass concept:

 Thing circle1 = new Circle(10,10);
 Thing square = new Square(0,0,10,10);
 Thing circle2 = new Circle(50,100);

now add them to the list and use in the same loop.

 List<Thing> list = new ArrayList<>();
 list.addAll(square, circel1, circle2);

 for( thing: list) {
    thing.draw()
 }
Sergey Grinev
  • 34,078
  • 10
  • 128
  • 141