Concrete Vs Abstract Vs Interface - [OOP & Java #4]
Tags: computerscience, java, oop
Published: 2020-09-15 12:31:09

Let's explore the relationship between concrete class, abstract class, and interfaces.

Suppose we would like to create a representation of a laptop.
```java
class Laptop {
  String screen;
  int batteryHour;

  Laptop(String screen, int batteryHour) {
    this.screen = screen;
    this.batteryHour = batteryHour;
  }

  void moveCursor(){
    System.out.println("Using the trackpad to move the cursor");
  }
}
```
The `Laptop` class contains a constructor that takes in a string representing the resolution of its screen and an integer representing the hours that its battery can last. It also has a `moveCursor` method for illustration purposes later on.
```java
Laptop ex1 = new Laptop("1080P",5);
Laptop ex2 = new Laptop("4k", 8);
```

---
A typical issue we often face is that sometimes we need to create something related to the existing objects, like a desktop in this case.
Given that a desktop also has a screen, we can *simply* and *intuitively* make our laptop class the parent of the desktop class, like so:

```java
class Desktop extends Laptop {
  Desktop(String screen, int batteryHour){
    super(screen, batteryHour);
  }
}
```

If you read the book "Thinking Fast And Slow", you will know that system 1, or the lazy, quick response system of our brain, often make mistakes. 

This is one such instance. So, **DO NOT** in any circumstances, subclass an object simply because they share some properties. There are many reasons why the above solution might violate the Liskov Substitution Principle (LSP). One obvious point is that the `batteryHour` does not make sense to a `Desktop`.

Both laptops and desktops are computers. They share a lot of similarities. However, you would agree with me that we do not typically say that a desktop is a laptop or vice versa. This is a tell-tale sign that they are not suitable for a parent-child relationship. 

---
#### Concrete Class
Knowing that they are both computers leads us to the first valid design:
```java
// Have Computer as a Concrete parent class
class Computer {
  String screen;
  void moveCursor(){
    System.out.println("moving the cursor?");
  }
}

class Laptop extends Computer {
  int batteryHour;
  Laptop(String screen, int batteryHour) {
    super.screen = screen;
    this.batteryHour = batteryHour;
  }
  
  @Override
  void moveCursor(){
    System.out.println("Using the trackpad to move the cursor");
  }
}

class Desktop extends Computer {
  Desktop(String screen){
    super.screen = screen;
  }

  @Override
  void moveCursor(){
    System.out.println("Using the mouse to move the cursor");
  }   
}
```
Observations
- Define a `Computer` class as the parent of these two child class
- For properties and methods that are common, they should be extracted into the parent class.
- Methods can be shared among the children, and by including them in the parent class, we make sure that all its children will have those methods. However, we may or may not be able to fill in the implementation of a method accurately at the parent class level. This means each child class will need to override this method to include its own implementation. Note that this behavior is not enforced by the compiler, meaning forgetting to override a method when you meant to do so will result in an unexpected runtime error. For example, desktop users typically do not have/use a trackpad to control their cursor. Therefore, `moveCursor` method has to be overridden at the child class level for `Desktop`. Yet, to omit the overriding is perfectly acceptable behavior to the compiler.
- It is also possible to instantiate the parent class since it is concrete. But, it may not make sense to do so. In our case, if we call `new Computer().moveCursor()`, the output may not be meaningful.

---
#### Abstract Class
The second approach is to create `Computer` as an abstract class.
```java
abstract class Computer {
  String screen;
  abstract void moveCursor();
}

class Laptop extends Computer {
  int batteryHour;
  Laptop(String screen, int batteryHour) {
    super.screen = screen;
    this.batteryHour = batteryHour;
  }
  
  @Override
  void moveCursor(){
    System.out.println("Using the trackpad to move the cursor");
  }
}

class Desktop extends Computer {
  Desktop(String screen){
    super.screen = screen;
  }

  @Override
  void moveCursor(){
    System.out.println("Using the mouse to move the cursor");
  }   
}
```
##### Abstract class
- Cannot be instantiated
- Contains abstract methods that do not have implementation details and must be implemented by its subclass, will be enforced by the compiler
- Can contain properties
- Can contain concrete methods with implementations as well

This pretty much solves the few issues we have with concrete class. If the parent class does not need to be instantiated, it will be safe to say that letting the parent class be abstract is *usually* better. Now, the drawback of the second approach, at least for Java, is that a class can only inherit from **one** parent class. 

Let say you want to have a `Chargeable` class for objects that can be charged:
```java
abstract class Chargeable {
  abstract Chargeable charge();
}
```
Then you are faced with a dilemma. Given that a class can only subclass one parent class, which one to choose?

##### Side note on multiple inheritance
- You may want to inherit all methods of different parents, but not all properties.
- Will `PineapplePen` be a logical object to you?
```java
class PineapplePen extends Pineapple, Pen{
//...
}
```
---
#### Interfaces
The next option in line is interfaces:
```java
abstract class Computer {
  String screen;
  abstract void moveCursor();
}

interface Chargeable {
  void charge();
}

class Laptop extends Computer implements Chargeable {
  int batteryHour;
  Laptop(String screen, int batteryHour) {
    super.screen = screen;
    this.batteryHour = batteryHour;
  }
  
  @Override
  void moveCursor(){
    System.out.println("Using the trackpad to move the cursor");
  }

  @Override
  public void charge(){
    // pretend that it does something
    System.out.println("Charging the laptop");
  }
}

class Desktop extends Computer implements Chargeable {
  Desktop(String screen){
    this.screen = screen;
  }

  @Override
  void moveCursor(){
    System.out.println("Using the mouse to move the cursor");
  }
  
  @Override
  public void charge(){
    // pretend that it does something
    System.out.println("Providing power to the desktop");
  }
}   
```
Interfaces have the following characteristics:
- Methods are implicitly abstract and public
- Cannot be instantiated
- Only constant variables allowed (static final) within interfaces
- Interface can extend multiple interfaces
- A class can implement multiple interfaces

In the final code snippet, there is a combination of concrete class, abstract class, and interface at work. There is no dominance of one over the other. Rather, exploring situations in which one of them will be most helpful will provide us the knowledge to weigh our options and choose the right one when designing our program.

---
### Summary
|                | Concrete Class | Abstract Class      | Interface |
|----------------|----------------|---------------------|-----------|
| Implementation | Actual         | Actual <br>Abstract | Abstract  |
| Instantiatable | Yes            | No                  | No        |

Till next time...