Skeletal Implementations in Java Explained

I use interfaces generously in my programs. I prefer them over using abstract classes for several reasons, some of which I will mention below:

  1. Inheritance does not promote good encapsulation. All sub classes depend on the implementation details of the super class. This may result in broken sub classes when the super class is changed. (Imagine testing all sub classes every time you change the super class!)
  2. Unlike inheritance, where a sub class can only extend from one super class, classes are free to implement as many interfaces as they like to support.
  3. It is very easy to support a new interface in an existing class. Suppose you would like several of your classes to support a new type, say, Serializable. You can simply implement the interface in your classes and define the interface methods. For example, any class in Java can implement the Comparable interface and can be applied everywhere a Comparable type is expected.
    Note: This is called defining a mixin[1]. Comparable is a mixin type. These types are intended to be used by other classes to provide additional functionality.

The above three arguments go directly against the philosophy of abstract classes. A sub class can only have one parent class and abstract classes defeat the purpose of mixins (Imagine Comparable being an Abstract class).

Now that I have tried my best to convince you Inheritance is bad, let me say this:

“Inheritance has its own place in programming. It is helpful in many cases, and decreases programming effort”

This is best explained with an example. Suppose you are writing a program, which uses Redis to represent its data. You would like to create specialized classes that deal with certain types of data. For instance, a class could be created to open a connection to Redis Database #0 to store running counters and perform all related actions. Another class would connect to Redis Database #1 and store all users in a set who have requested to opt out from the service.

Let us define an Interface representing the main Redis Database:

interface RedisConnection {

    int connect();

    boolean isConnected();

    int disconnect();

    int getDatabaseNumber();
}

Lets write a Counters class which implement this interface:

class RedisCounters implements RedisConnection {

    @Override
    public int connect() {
        //... lots of code to connect to Redis
    }

    @Override
    public boolean isConnected() {
        //... code to check Redis connection
    }

    @Override
    public int disconnect() {
        //... lots of code to disconnect & perform cleanup
    }
 }

Finish by writing a class, which deals with users who have chosen to Opted Out in Redis.

class RedisOptOut implements RedisConnection {

    @Override
    public int connect() {

        //... lots of code to connect to Redis
    }

    @Override
    public boolean isConnected() {
        //... code to check Redis connection
    }

    @Override
    public int disconnect() {
       //... lots of code to disconnect & perform cleanup
    }

    	/**
      * Other code specific to handling users who have opted out
      */

    // method specific to this class
    public boolean isOptedOut(String userid) {….}
}

If you look closely at the two classes above, you’ll notice something is not right: both classes repeat the connect(), isConnected() and disconnect() functions verbatim. This type of code repetition is not good for several obvious reasons: imagine if you have 10 classes instead of just two, and you would like to change the way connect() function works. You’ll have to make edits in all 10 classes and test them.

Abstract Classes To the Rescue

The program in the last section, presents a classic case where abstract classes excel. We can have define an abstract super class which implement common functionality and make its methods final to restrict sub classes from overriding them. You’ll end up with some like the following:

abstract class RedisConnection {
	public final int connect() {
		// ... lots of code to connect to Redis
	}

	public final boolean isConnected() {
		//... code to check Redis connection
	}

	public final int disconnect() {
		// ... lots of code to disconnect from Redis and perform cleanup
	}
}

/**
 *  sub class which extends from RedisConnection
 *
 */
class RedisCounts extends RedisConnection {

	/**
	 * There is no need to define connect(), isConnected() and disconnect() as
	 * these functions are defined by the super class.
	 */

	/**
	 * Other code specific to storing and retreiving counters
	 */
}

/**
 * another sub class extending from RedisConnection
 *
 */
class RedisOptOut extends RedisConnection {
	/**
	 * There is no need to define connect(), isConnected() and disconnect() as
	 * these functions are defined by the super class.
	 */

	/**
	 * Other code specific to handling users who have opted out
	 */
}

No doubt, this is a better solution. But at the beginning of this post, I explained why interfaces are preferred over inheritance. Let us take this one step further and combine interfaces and abstract classes, to maximize the benefits.

Abstract Classes + Interfaces = Abstract Interfaces

We can combine Abstract Classes and Interfaces by providing an abstract class, which defines the basic functionality, with every interface where necessary. The interface defines the type, whereas the abstract class does all the work implementing it.

By convention, these classes are named: AbstractInterface [Interface is the name of the interface the abstract class is implementing]. This convention comes from Java. In the Collections API, the abstract class, which goes with the List interface, is called AbstractList, etc.

The key to designing these abstract classes or AbstractInterfaces is to design them properly and document it well for the programmers. For example, the class comment of the java.util.AbstractList class define the methods the programmers need to override in their implementations:

“To implement an unmodifiable list, the programmer needs only to extend this class and provide implementations for the get(int) and size() methods.
To implement a modifiable list, the programmer must additionally override the set(int, E) method (which otherwise throws an UnsupportedOperationException). If the list is variable-size the programmer must additionally override the add(int, E) and remove(int) methods.”[2]

Abstract Interfaces (Interfaces + Abstract Classes) give programmers the freedom to choose whether they would like to implement the interface directly or extend the abstract class. In our example, we will have:

/**
 * The Interface
 *
 */
interface RedisConnection
{
    int connect();
    boolean isConnected();
    int disconnect();
    int getDatabaseNumber();
}

/**
 * Abstract class which implements the interface.
 * This is called Abstract Interface
 *
 */
abstract class AbstractRedisConnection implements RedisConnection
{
    @Override
    public final int connect()
    {
        //... lots of code to connect to Redis
    }

    @Override
    public final boolean isConnected()
    {
        //... code to check Redis connection
    }

    @Override
    public final int disconnect()
    {
        //... lots of code to disconnect from Redis and perform cleanup
    }
 }

/**
 * A subclass which extends from the Abstract Interface
 *
 */
class RedisOptOut extends AbstractRedisConnection {…}

In cases where a class cannot extend from the AbstractInterface directly, it can still implement the Interface, and use an inner class which extends from the AbstractInterface and forward all interface method invocations to the inner class. For example:

/**
 * A class showing the forwarding technique. This class implements
 * an interface, but forwards all interface method invocations
 * to an abstract class, the Abstract Interface.
 */
class RedisCounters implements RedisConnection {

	// inner class extending Abstract Interface
	private class RedisConnectionForwarder extends AbstractRedisConnection {
		public void RedisConnectionForwarder() {
		}
	}
	RedisConnectionForwarder r = new RedisConnectionForwarder();

	@Override
	public int connect() {
		// Simply forward the request to the Forwarding class.
		return r.connect();

	}

	@Override
	public boolean isConnected() {
		// Simply forward the request to the Forwarding class.
		return r.isConnected();
	}

	@Override
	public int disconnect() {
		// Simply forward the request to the Forwarding class.
		return r.disconnect();
	}

	/**
	 * Other code specific to storing and retreiving **counters**
	 */
}

In cases where a class cannot extend from the AbstractInterface directly, it can still implement the Interface, and use an inner class which extends from the AbstractInterface and forward all interface method invocations to the inner class. For example:

/**
 * A class showing the forwarding technique. This class implements
 * an interface, but forwards all interface method invocations
 * to an abstract class, the Abstract Interface.
 */
class RedisCounters implements RedisConnection {

	// inner class extending Abstract Interface
	private class RedisConnectionForwarder extends AbstractRedisConnection {
		public void RedisConnectionForwarder() {
		}
	}
	RedisConnectionForwarder r = new RedisConnectionForwarder();

	@Override
	public int connect() {
		// Simply forward the request to the Forwarding class.
		return r.connect();

	}

	@Override
	public boolean isConnected() {
		// Simply forward the request to the Forwarding class.
		return r.isConnected();
	}

	@Override
	public int disconnect() {
		// Simply forward the request to the Forwarding class.
		return r.disconnect();
	}

	/**
	 * Other code specific to storing and retreiving **counters**
	 */
}

As a final technique, you can also use static factories returning concrete instances, which they implement, in form of anonymous inner classes.  For example:

/**
 * A static factory method
 */
public static RedisConnection getRedisCountersImpl(…)
{
	return new AbstractRedisConnection() {
		//...
        /**
	 * Other code specific to storing and retrieving counters
	 */

	}
}

Summary

Using Interfaces, as a general contract, has many benefits over Inheritance. Inheritance, however has its own place in programming, and often times is a necessary evil. In this post, we explored Abstract Interfaces which combine the power of Interfaces with Inheritance. Abstract Interface is a term for Abstract Class, which implements all the functionality of an Interface. Abstract Interfaces always go with the Interfaces they are supporting.

Abstract Interfaces gives programmers the freedom to use either the interface or the abstract class, instead of tying them with inheritance down like abstract classes. We explored two techniques of using abstract classes when extending from the Abstract Interface is not possible. The Java API uses abstract Interfaces graciously. The Collections API is filled with these: AbstractList, AbstractSet, AbstractMap, AbstractCollection.

References

[1] http://en.wikipedia.org/wiki/Mixin

[2] http://docs.oracle.com/javase/6/docs/api/java/util/AbstractList.html

4 thoughts on “Skeletal Implementations in Java Explained

Leave a reply to Java Multithreading Steeplechase: Executors | 10K-LOC Cancel reply