Abstract class and Interface usage


Abstract class and Interface usage: "Is-a" relation vs "Has-a" capability

When to use abstract classes: To implement the same or different behaviour among multiple related objects

When to use interfaces: to implement a contract by multiple unrelated objects

Abstract classes create "is a" relations while interfaces provide "has a" capability.

This can be seen in the code below:

public class DemoApp {
    public static void main(String[] args) {
 
        MyDog myDog = new MyDog("Buddy", 3);
        MyCat myCat = new MyCat("Whiskers", 5);
 
        System.out.println("MyDog: " + myDog);
        System.out.println("MyCat: " + myCat);
 
        myDog.remember();
        myDog.protectOwner();
 
        Learn learningDog = myDog;
        learningDog.learn();
 
        myCat.remember();
        myCat.protectOwner();
 
        Climb climbingCat = myCat;
        climbingCat.climb();
 
        Human human = new Human("John", 30);
        System.out.println(human);
 
        Climb climbingHuman = human;
        climbingHuman.climb();
 
        Think thinkingHuman = human;
        thinkingHuman.think();
 
        Learn learningHuman = human;
        learningHuman.learn();
 
        Apply applyingHuman = human;
        applyingHuman.apply();
    }
}
 
abstract class MyAnimal {
    String name;
    int lifeExpectancy;
 
    public MyAnimal(String name, int lifeExpectancy) {
        this.name = name;
        this.lifeExpectancy = lifeExpectancy;
    }
 
    public abstract void remember();
 
    public abstract void protectOwner();
 
    @Override
    public String toString() {
        return this.getClass().getSimpleName() + ":" + name + ":" + lifeExpectancy;
    }
}
 
class MyDog extends MyAnimal implements Learn {
 
    public MyDog(String name, int age) {
        super(name, age);
    }
 
    @Override
    public void remember() {
        System.out.println(this.getClass().getSimpleName() + " can remember for 5 minutes");
    }
 
    @Override
    public void protectOwner() {
        System.out.println(this.getClass().getSimpleName() + " will protect owner");
    }
 
    @Override
    public void learn() {
        System.out.println(this.getClass().getSimpleName() + " can learn");
    }
}
 
class MyCat extends MyAnimal implements Climb {
 
    public MyCat(String name, int age) {
        super(name, age);
    }
 
    @Override
    public void remember() {
        System.out.println(this.getClass().getSimpleName() + " can remember for 16 hours");
    }
 
    @Override
    public void protectOwner() {
        System.out.println(this.getClass().getSimpleName() + " won't protect owner");
    }
 
    @Override
    public void climb() {
        System.out.println(this.getClass().getSimpleName() + " can climb");
    }
}
 
interface Climb {
    void climb();
}
 
interface Think {
    void think();
}
 
interface Learn {
    void learn();
}
 
interface Apply {
    void apply();
}
 
class Human implements Think, Learn, Apply, Climb {
    String name;
    int age;
 
    public Human(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    @Override
    public void think() {
        System.out.println("I can think: " + this.getClass().getSimpleName());
    }
 
    @Override
    public void learn() {
        System.out.println("I can learn: " + this.getClass().getSimpleName());
    }
 
    @Override
    public void apply() {
        System.out.println("I can apply: " + this.getClass().getSimpleName());
    }
 
    @Override
    public void climb() {
        System.out.println("I can climb: " + this.getClass().getSimpleName());
    }
 
    @Override
    public String toString() {
        return "Human: " + name + ":Age:" + age;
    }
}

Output:

MyDog: MyDog:Buddy:3
MyCat: MyCat:Whiskers:5
MyDog can remember for 5 minutes
MyDog will protect owner
MyDog can learn
MyCat can remember for 16 hours
MyCat won't protect owner
MyCat can climb
Human: John:Age:30
Human can climb: Human
Human can think: Human
Human can learn: Human
Human can apply: Human

Consider using abstract classes if...

  • You want to share code among several closely related classes.
  • You expect that classes that extend your abstract class have many common methods or fields, or require access modifiers other than public (such as protected and private).
  • You want to declare non-static or non-final fields.

Consider using interfaces if...

  • You expect that unrelated classes would implement your interface. For example, many unrelated objects can implement the Serializable interface.
  • You want to specify the behaviour of a particular data type but are not concerned about who implements its behaviour.
  • You want to take advantage of multiple inheritance of type.

Static Inheritance

Static method can be inherited similar to normal methods, however unlike normal methods it is impossible to create "abstract" methods in order to force static method overriding. Writing a method with the same signature as a static method in a super class appears to be a form of overriding, but really this simply creates a new function hides the other.

public class ParentClass {
 
 public static int number = 10;
 public static void greet() {
 System.out.println("Greetings");
 }
 
 public static void main(String[] args) {
 ParentClass.greet();
 System.out.println("ParentClass's number: " + ParentClass.number);
 
 ChildClass.greet();
 // This will be different than the above statement's output since it runs
 // a different method
 ChildClass.greet(true);
 
 StaticOverride.greet();
 System.out.println("StaticOverride's number: " + StaticOverride.number);
 }
}
 
public class ChildClass extends ParentClass {
 
 // Inherits the greet function but does not override it 
 public static void greet(boolean test) {
 System.out.println("Hey there");
 }
}
 
public static class StaticOverride extends ParentClass {
 // Hides the number field from ParentClass
 // You can even change the type, as this doesn't affect the signature
 public static String number = "modified";
 
 // Cannot use @Override annotation since this is static
 // This overrides the greet method from ParentClass
 public static void greet() {
 System.out.println("Static says Hi");
 }
}

Running any of these classes produces the output:

Greetings
ParentClass's number: 10
Greetings
Hey there
Static says Hi
StaticOverride's number: modified

Note that unlike normal inheritance, in static inheritance methods are not hidden. You can always call the base sayHello method by using BaseClass.sayHello(). But classes do inherit static methods if no methods with the same signature are found in the subclass. If two method's signatures vary, both methods can be run from the subclass, even if the name is the same.

Basic Programs