Thursday, June 10, 2021

Java OOPs: Inheritance

Chapters

Java Inheritance

Inheritance is part of java OOPs(Object Oriented Programming System) or OOP for short. It is the process in java in which one class can inherit inheritable another class. In java, we can achieve inheritance by using the extends or implements keyword.

The extends Keyword

When we want a class to inherit another class then we will use the extends keyword.
Example

public class SampleClass{

  public static void main(String[]args){
    
    Cat cat = new Cat();
    System.out.println("Cat is a " + cat.getType());
    System.out.println("Cat name: " + cat.getName());
    //private class members can't be inherited
    //error
    //System.out.println(cat.type);
    
    Animal animal = new Animal();
    System.out.println("Animal type: " + animal.getType());
    //subclass members are not visible to 
    //super(parent or base) class
    //error
    //System.out.println("Animal name: " + animal.getName());
  }
}

class Animal{
  private String type = "Mammal";
  
  public String getType(){
    return type;
  }

}

class Cat extends Animal{
  private String name = "Kitty";
  
  public String getName(){
    return name;
  }
}
In the example above, we see that Cat class inherits the getType() method of Animal Class but Cat class can't inherit(access) the "type" variable of Animal because it's private. We can also see that super class can't access sub class members.

The class whose members are inherited by another class is the super(base or parent) class. The class that inherits another class members is the sub(child, derived or extended) class. A class(except for enum class) can only explicitly extend one class. Java doesn't support multiple inheritance of classes.

If we don't want a parent class to be instantiated then we can make the parent class as abstract class.
Example

public class SampleClass{

  public static void main(String[]args){
    
    Cat cat = new Cat();
    System.out.println("Cat is a " + cat.getType());
    System.out.println("Cat name: " + cat.getName());
    
    //Abstract class can't be instantiated
    //error
    //Animal animal = new Animal();

  }
}

abstract class Animal{
  private String type = "Mammal";
  
  public String getType(){
    return type;
  }

}

class Cat extends Animal{
  private String name = "Kitty";
  
  public String getName(){
    return name;
  }
}
Interface can also use extends keyword but it only works on another interface.
interface SourceChecker{

  void verifySource();
}

interface SourceFilter extends SourceChecker{
  
  void filterSource();
}
In generics, extends keyword can be used to create a bounded type parameters and upper bounded wildcard.

The implements Keyword

We use the implements keyword to make a class to be a sub class of an interface.
public class SampleClass{

  public static void main(String[]args){
  
    new Animal().makeSound();
  }
}

interface AnimalSound{

  void makeSound();
}

class Animal implements AnimalSound{

  @Override
  public void makeSound(){
    System.out.println("Default Sound...");
  }
}
In the example above, Animal parent class(interface) is AnimalSound and we overrode AnimalSound's makeSound() method.

Unlike the extends keyword, we can use the implements keyword to implement multiple interfaces to a class. To implement multiple interfaces to a class, write the implements keyword then after that, write interface names and separate them by comma.
public class SampleClass{

  public static void main(String[]args){
    Cat cat = new Cat();
    cat.makeSound();
    cat.internalSkeleton();
  }
}

interface AnimalSound{

  void makeSound();
}

interface SkeletalSystem{
  
  void internalSkeleton();
  void exoSkeleton();
}

//This class has multiple parent classes(interfaces)
abstract class Animal implements AnimalSound,SkeletalSystem{

  @Override
  public void makeSound(){}
  @Override
  public void internalSkeleton(){}
  @Override
  public void exoSkeleton(){}
  
}

class Cat extends Animal{

  @Override
  public void makeSound(){
  
    System.out.println("Meow!");
  }
  
  @Override
  public void internalSkeleton(){
    System.out.println("Cat has internal skeleton!");
  }
}
extends and implements can be used at the same in a class
public class SampleClass{
 
 public static void main(String[]args){
 }
}

interface Interface{
}

class ClassA{
}

class ClassB extends ClassA implements Interface{
}
Remember, parent class is not instantiated during inheritance. Only the instance of subclass with superclass members is created. Though, sub class type can be casted to its parent class type. We will learn about casting objects in typecasting(Objects) topic.

In generics, the super keyword is used to create a lower bounded wildcard.

Object Class Implicit Inheritance

The Object class is the parent of all classes. Every class has Object as a superclass. Thus, Object class is implicitly inherited by user-defined classes. Take a look at this example:
public class SampleClass{

  public String ObjectToString(){
    return super.toString();
  }
  
  @Override
  public String toString(){
    return "SampleClass ToString()";
  }
  
  public static void main(String[]args){
    SampleClass sc = new SampleClass();
    System.out.println("Obj toString(): " +
                       sc.ObjectToString());
    System.out.println("SampleClass toString(): " +
                       sc.toString());
  }
}
As you can see in the example above, we can override toString() which is one of Object class members and we can call the toString() of Object class.

Implicit Call to super() During Inheritance

If we don't call super() explicitly in a constructor then super() is implicitly called in a constructor of a subclass when we do inheritance. Implicit call of super() calls the default constructor of a parent class whether it is defined or not.
public class SampleClass{

  public static void main(String[]args){
    ClassB cb = new ClassB("String");
  }
}

class ClassA{

  ClassA(){
    System.out.println("ClassA Default Constructor");
  }
  
  ClassA(String a){
    System.out.println(a);
  }
}

class ClassB extends ClassA{

  ClassB(){
    System.out.println("ClassB Default Constructor");
  }
  
  ClassB(String a){
    System.out.println(a);
  }
}

Result
ClassA Default Constructor
String
Try calling the default constructor of ClassB and the default constructor of ClassA will still be implicitly called. Also, try adding private keyword before the default constructor name of ClassA and you will encounter a compile-time error.

Types of Inheritance
There are various inheritance types that Java supports.

Single Inheritance

Simpliest form of inheritance. This inheritance is comprised of one parent class and one sub class.
public class SampleClass{

  public static void main(String[]args){
    ClassB b = new ClassB();
  }
}

abstract class ClassA{

  ClassA(){
    System.out.println("Class A");
  }
}
  
class ClassB extends ClassA{

  ClassB(){
    System.out.println("Class B");
  }
}
Single Inheritance Diagram
Single Inheritance

Multilevel Inheritance

This inheritance is comprised of a parent or root class, sub/parent class and sub class.
public class SampleClass{

  public static void main(String[]args){
    ClassC c = new ClassC();
  }
}

//parent or root class
abstract class ClassA{
  String name = "ClassA";
  int num1 = 1;
  
  ClassA(){
    System.out.println(name + " Constructor");
    System.out.println("number : " + num1);
    System.out.println();
  }
}

//sub/parent class
abstract class ClassB extends ClassA{
  String name = "ClassB";
  String parent = super.name;
  int num2 = 2;
  
  ClassB(){
    System.out.println(name + " Constructor");
    System.out.println("number : " + num2);
    System.out.println("ClassB Parent: " + parent);
    System.out.println("number : " + num1);
    System.out.println();
  }
}

//sub class
class ClassC extends ClassB{
  String name = "ClassC";
  String parent = super.name;
  int num3 = 3;
  
  ClassC(){
    System.out.println(name + " Constructor");
    System.out.println("number : " + num3);
    System.out.println("ClassC Parent: " + parent);
    System.out.println("number : " + num2);
    
    //Won't work. sub class can't directly access Parent's parent
    //class hidden members
    //System.out.println(super.name + " Parent: " + super.super.name);
    
    System.out.println(super.name + " Parent: " + super.parent);
    System.out.println("number : " + num1);
    System.out.println();
  }
}

Result
ClassA Constructor
number: 1

ClassB Constructor
number: 2
ClassB Parent: ClassA
number: 1

ClassC Constructor
number: 3
ClassC Parent: ClassB
number: 2
ClassB Parent: ClassA
number: 1
In the example above, we can see that ClassC inherits inheritable members of ClassB and ClassC. We can also see that ClassC can't directly access ClassA hidden members. In this example, ClassB is used as a bridge in order to pass the value of name variable of ClassA to ClassC.
Shadowing or member hiding is discussed in the this keyword topic.

Multilevel Inheritance Diagram
Multilevel Inheritance

Hierachical Inheritance

This inheritance is comprised of a parent class and multiple child classes.
public class SampleClass{

  public static void main(String[]args){
  
    ClassB b = new ClassB();
    ClassC c = new ClassC();
  }
}

class ClassA{
  String name = "ClassA";
  
  ClassA(){
    System.out.println(name + " Constructor");
    System.out.println();
  }
}

class ClassB extends ClassA{
  String name = "ClassB";
  
  ClassB(){
    System.out.println(name + " Constructor");
    System.out.println(name + " Parent: " + super.name);
    System.out.println();
  }
}

class ClassC extends ClassA{
  String name = "ClassC";
  
  ClassC(){
    System.out.println(name + "Constructor");
    System.out.println(name + " Parent: " + super.name);
    System.out.println();
  }
}
Hierarchical Inheritance Diagram
Multilevel Inheritance

Multiple Inheritance(Interface Only)

Java doesn't support multiple parent classes. However, a sub class can have multiple interfaces as parent class.
public class SampleClass{
  
  public static void main(String[]args){
  
    ClassC c = new ClassC();
    c.message1();
    c.message2();
  }
}

interface InterfaceA{
  default void message1(){
    System.out.println("InterfaceA");
  }
}
  
interface InterfaceB{
  default void message2(){
    System.out.println("InterfaceB");
  }
}
  
class ClassC implements InterfaceA,InterfaceB{
  
  ClassC(){
    System.out.println("ClassC Constructor");
  }
}
Default methods in interface can be used since Java8.

Multiple Inheritance Diagram
Multiple Inheritance

Hybrid Inheritance

This inheritance comprises two or more types of inheritance.
public class SampleClass{

  public static void main(String[]args){
  }
}

interface InterfaceA{
}

interface InterfaceB{
}

//multiple inheritance
class ClassC implements InterfaceA,InterfaceB{
}

//single inheritance
class ClassD extends ClassC{
}

//hierarchical inheritance
class ClassE extends ClassD{
}

//hierarchical inheritance
class ClassF extends ClassD{
}

Hybrid Inheritance Diagram
Hybrid Inheritance

No comments:

Post a Comment