Abstract Class vs Interface

There is always a question about when we need to use abstract class, and when to use interface. For me, it's an open-ended question, can only be achieved in learning by doing, understand about application design, thinking abstractly, and for sure... coding.

Here the difference between abstract class and interfaces:

  • An abstract class may contain non-final variables, but all variables declared in a Java interface are by default final.
  • A Java abstract class can have any access modifiers like private, protected, etc, but members of a Java interface are public by default. (private methods in Java interfaces is introduced in Java 9, check here)
  • An abstract class can extend another Java class and implement multiple Java interfaces, but a Java interface only can extend another Java interface.

Before Java 8, the difference between abstract class and interface is very clear:

  • A Java abstract class can have either abstract methods and/or even complete (concrete) methods, but methods in a Java interface must be abstract and cannot have implementations. (default and static methods in an interfaces is introduced in Java 8, check here)

About the usage in subclass:

  • A Java abstract class should be extended using keyword "extends", but a Java interface should be implemented using keyword "implements".
  • A Java class only can extend one abstract class, but can implement multiple interfaces.
SampleInterface.java
public interface SampleInterface {
 
    public String finalString = "Must defined something";
    public static String finalStaticString = "Must defined something static";
    
    abstract void abstractMethod(String input);
}
                    

SampleAbstractClass.java
public abstract class SampleAbstractClass {

    public String nonFinalString;
    public static String nonFinalStaticString;
    
    abstract void abstractMethod(String input);
    
    void concreteMethod(String input) {
        // concrete steps
    }
}
                    

AnotherAbstractClass.java
public abstract class AnotherAbstractClass implements SampleInterface {

    // if not public will encounter error:
    // abstractMethod(String) in AnotherAbstractClass 
    // cannot implement abstractMethod(String) in SampleInterface
    // attempting to assign weaker access privileges; was public
    @Override
    public abstract void abstractMethod(String input);
}
                    

In terms of application design, Abstract class is used to implement Template Method pattern, when interface is used (among others) to implement the Observer pattern.

  • Template Method pattern is a behavioral design pattern that defines the program skeleton of an algorithm in an operation, deferring some steps to sub classes. It lets one redefine certain steps of an algorithm without changing the algorithm's structure.
  • Observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

Referring to the "Gang of Four" book Design Patterns, the interface is used to apply the Observer pattern. Since in Java, the data type is determined by the interface and class (and it's super class), so interface is used if one class wants to have several data types.

ConcreteClass.java
public class ConcreteClass extends AnotherAbstractClass {

    @Override
    public void abstractMethod(String input) {
        // implement method here
    }    
}
                    

AbstractionApp.java
public class AbstractionApp {
    
    public static void main(String[] args) {
        ConcreteClass c = new ConcreteClass();
        System.out.println("c instanceof Object: " + (c instanceof Object));
        System.out.println("c instanceof ConcreteClass: " + (c instanceof ConcreteClass));
        System.out.println("c instanceof AnotherAbstractClass: " + (c instanceof AnotherAbstractClass));
        System.out.println("c instanceof SampleInterface: " + (c instanceof SampleInterface));
    }
}

/* 
Output:
------    
c instanceof Object: true
c instanceof ConcreteClass: true
c instanceof AnotherAbstractClass: true
c instanceof SampleInterface: true
*/
                    

Here in our sample, the type of c are Object (hey, do you know why?), ConcreteClass, AnotherAbstractClass, and SampleInterface.


Let's go to our next example:

Validator.java
public interface Validator {
    
    List<String> validate(Object o);
}
                    

PersonValidator.java
import java.util.ArrayList;
import java.util.List;

public class PersonValidator implements Validator {
 
    @Override
    public List<String> validate(Object person) {
        List<String> errors = new ArrayList<>();
        // something in between
        return errors;
    }
}
                    

AddressValidator.java
import java.util.ArrayList;
import java.util.List;

public class AddressValidator implements Validator {
 
    @Override
    public List<String> validate(Object address) {
        List<String> errors = new ArrayList<>();
        // something in between
        return errors;
    }
}
                    

BaseRules.java
import java.util.ArrayList;
import java.util.List;

public abstract class BaseRules {
    
    private final List<Validator> validators = new ArrayList<>();

    /**
     * @return the validators
     */
    public List<Validator> getValidators() {
        return validators;
    }
    
    public final List<String> validate() {
        List<String> errors = new ArrayList<>();
        for (Validator validator : this.validators) {
            errors.addAll(validator.validate(validator));
        }
        return errors;
    }
}
                    

LoanRules.java
public class LoanRules extends BaseRules {

    public LoanRules() {
        this.getValidators().add(new PersonValidator());
        this.getValidators().add(new AddressValidator());
    }    
}
                    

PersonValidator and AddressValidator are Validator. All Validator type can be added into validators.

Then, we also create BaseRules as an abstract class, since we want to create Template Method. By defining the steps of algorithm using abstract operations, the template method fixes their ordering, but it lets you vary the steps to suit their needs. The Template Method is one of the concrete methods and (preferably) final. In our example, validate() method is final. LoanRules is concrete class, where PersonValidator and AddressValidator are registered in the validators.

Real Example

Some example above will give you some pointer when to use interface and when to use abstract class.

But let's look at one real example, class AbstractController

public abstract class AbstractController
extends WebContentGenerator
implements Controller

Convenient superclass for controller implementations, using the Template Method design pattern.

Workflow (and that defined by interface):

  1. handleRequest() will be called by the DispatcherServlet
  2. Inspection of supported methods (ServletException if request method is not support)
  3. If session is required, try to get it (ServletException if not found)
  4. Set caching headers if needed according to the cacheSeconds property
  5. Call abstract method handleRequestInternal() (optionally synchronizing around the call on the HttpSession), which should be implemented by extending classes to provide actual functionality to return ModelAndView objects.

As you see, AbstractController is an abstract class using the Template Method design pattern. Why is it made abstract? Because when writing the code, the programmer (Rod Johnson) doesn't know what we want to do when the controller is processed. Therefore, the complete method is up to the programmer who complete the implementation. As example, one method that the concrete class need to implements is handleRequestInternal()

protected abstract ModelAndView handleRequestInternal(
            HttpServletRequest hsr, HttpServletResponse hsr1) throws Exception;
                    

Conclusion

Which should you use, abstract classes or interfaces?

Consider using abstract classes if any of these statements apply to your situation:

  • You want to share code among several closely related classes. (hint: related)
  • 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). (hint: non public)
  • You want to declare non-static or non-final fields. This enables you to define methods that can access and modify the state of the object to which they belong. (hint: non-static or non-final)

Consider using interfaces if any of these statements apply to your situation:

  • You expect that unrelated classes would implement your interface. For example, the interfaces Comparable and Cloneable are implemented by many unrelated classes. (hint: unrelated)
  • You want to specify the behavior of a particular data type, but don't care about who implements its behavior. (hint: not concerned about implementation)
  • You want to take advantage of multiple inheritance of type. (hint: multiple inheritance)

But off course, you can use both in the same time, again it's depend on your design thinking. Happy coding!