Search This Blog

Thursday 27 August 2015

Interfaces versus Abstract classes : pre and post Java 8

I have been given a contract to implement in Java. As a developer one of the early design decisions needed is, do I want an interface or an abstract class at the base of my hierarchy. This question is very popular in the Java world and often always pops up when doing low level design.
There is no golden rule on when to use what. It is easily possible to find a group of developers who say:
  • Pure Interface looks so much clean.
  • Interface ensures you do not fall to Java's Single Inheritance restriction.
  • Contracts should say what and not how - making it an ideal use case for an interface.
Opposing arguments are mostly concerned with:
  • What if I want to provide a default implementation for some methods ?
  • I don't like my data objects hierarchy starting with an interface. I prefer a class at the base.
  • I don't foresee any use case requiring a second class to be extended by my classes.
  • What if I wanted to force all my implementations to use a certain implementation for one of the contracts.
  • With abstract classes at the base, it is easy to add a new method with a default implementation. This will ensure that all sub classes that do not care for this method don't break, while the affected ones can go ahead and override.
As guessed the people in the second category would prefer to see abstract classes win here. A common compromise, I have seen in most of the projects I have worked in involve:
  • Define all the services, Business objects behind interfaces.
  • Where there was the possibility (genuine) of some class hierarchies having common code, setup an abstract class, and move the common code there - something I have often seen in web controllers and earlier in servlets ( remember the pre - framework days).
  • If needed the design would have a combination of both - a pure interface that defined all the contracts and abstract classes that provided default implementations where possible.
The third scenario seems the safest way to do things as it allows us to have default implementations for some methods which if needed can be overridden by the sub classes. Sub classes can also choose to directly implement the interface, thus allowing for extending some other class. It does means some additional work on the code front though. And if a sub class needs some default implementation code, but also extend a different class, then we are stuck.

With Java 8, Oracle has introduced some major rework in inheritance mechanism, boosting the power of interfaces in Java.
The introduction of default methods will go a long way in easing up the inheritance chain in some of the projects (and could also confuse in other places).

I decided the best place to start with was the Java docs at Oracle:
An abstract class is a class that is declared abstract—it may or may not include 
abstract methods. Abstract classes cannot be instantiated, but they can be 
subclassed.
On a direct comparison between the two:
Abstract classes are similar to interfaces. You cannot instantiate them, and they may 
contain a mix of methods declared with or without an implementation. However, with 
abstract classes, you can declare fields that are not static and final, and define public, 
protected, and private concrete methods. With interfaces, all fields are automatically public, 
static, and final, and all methods that you declare or define (as default methods) are public. 
In addition, you can extend only one class, whether or not it is abstract, whereas you can 
implement any number of interfaces.
So when to use interfaces and when to use abstract classes ?
Again I am quoting from the documentation:
Consider using abstract classes if any of these statements apply to your situation:
    -> 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. This enables you to define methods
       that can access and modify the state of the object to which they belong.
    
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.
    -> You want to specify the behavior of a particular data type, but not concerned about who 
       implements its behavior.
    -> You want to take advantage of multiple inheritance of type.

An example of an abstract class in the JDK is AbstractMap, which is part of the 
Collections Framework. 
Its subclasses (which include HashMap, TreeMap, and ConcurrentHashMap) share many methods 
(including get, put, isEmpty, containsKey, and containsValue) that AbstractMap defines.
An example of a class in the JDK that implements several interfaces is HashMap, which 
implements the interfaces Serializable, Cloneable, and Map<K, V>. By reading this 
list of interfaces, you can infer that an instance of HashMap (regardless of the developer 
or company who implemented the class) can be cloned, is serializable (which means that it 
can be converted into a byte stream; see the section Serializable Objects), and has the 
functionality of a map. In addition, the Map<K, V> interface has been enhanced 
with many default methods such as merge and forEach that older classes that have implemented this 
interface do not have to define.

Note that many software libraries use both abstract classes and interfaces; the HashMap class implements 
several interfaces and also extends the abstract class AbstractMap.
Along with all the nuggets of information, the documentation talks of the new feature of default methods.
While the reason for the existance of this feature seems to be Lambdas, it kind of fits in nicely with the use case of providing some default implementations for the interface methods.
Consider the below code:
interface Contract {
   public void method1();
   public void commonMethod();
}

abstract class ContractBase implements Contract {
 
   @Override
   public void commonMethod() {
      
   }
}

class ContractImpl extends ContractBase {

   @Override
   public void method1() {
   }
   
}
Here we have an interface, but we would like to provide some default implementation for commonMethod(), one that we expect would help most of the implementations. With pre Java 8, we would have to create an abstract class and place the code there. All our implementations would now be sub classes of ContractBase and not implement Contract directly.
With Java8, default methods helps skip the abstract class.
interface Contract {
   public void method1();
   
   default public void commonMethod() {
      //the common code here
   }
}

class ContractImpl implements Contract {//extends ContractBase {

   @Override
   public void method1() {
   }
   
}
The reason for default methods : (again from Oracle documentation)
default methods enable you to add new functionality to the interfaces of your libraries and 
ensure binary compatibility with code written for older versions of those interfaces.
With the introduction of Lambdas, there was a lot of richness that could be added to the Collections interfaces. But adding a new method in such heavily used API meant Oracle would end up breaking all implementations of Collections interfaces everywhere. So to avoid colossal complaints from java developers, Oracle came up with the idea of default methods. This feature allowed them to define all the new Lambda based methods in existing API, as well as provide code for them in the interface, thus ensuring that all implementations everywhere stay as is and can also use the new APIs without any change in code.
Another new feature Oracle introduced for interfaces is to allow them to have static methods. Before Java 8, interface represented a contract - at an instance level. Thus all methods were associated with an object. Now,
interface Contract {
   public void method1();
   
   default public void commonMethod() {
      //the common code here
   }
   
   static void utilityMethod() {
      //This is simply an utility method - implicit public method 
   }
}
The advantage, according to Oracle is:
This makes it easier for you to organize helper methods in your libraries; you can keep 
static methods specific to an interface in the same interface rather than in a separate class.
I can relate to this. I remember defining static methods in the base abstract class on occasions, as I wanted the method to be close to the hierarchy as opposed to a Helper class. But this is more of a design call and needs to be evaluated on a case to case basis. So with this added power, there are some cases where an abstract class could be skipped from the design.

1 comment:

  1. This is a practical Java Interview question for Java developers, more important for beginners to test their OOP skill. Thanks

    ReplyDelete