Skip to main content

Java interfaces and the concept of multiple inheritance

Posted by hellofadude on January 18, 2014 at 3:07 PM PST

Interfaces are completely abstract classes in Java that provide you with a uniform way to properly delineate the structure or inner workings of your program from its publicly available interface, with the consequence being a greater amount of flexibility and reusable code as well as more control over how you create and interact with other classes. More precisely, they are a special construct in Java with the additional characteristic that allow you to perform a kind of multiple inheritance i.e. classes that can be upcast to more than one class; a technique from which you are severely restricted from undertaking (and with good reason) when working exclusively with classes.

To create an interface in Java, you simply use the special interface keyword like this:-

interface Transaction { ... }

You may include the public access identifier to the left of the interface keyword if the interface is defined in a file of the same name. Without the public keyword, the interface defaults to package access.
Interfaces have no implementation and therefore relieve you of any concerns pertaining to storage; this is to say your interface can only be implemented in a concrete class. When you create an interface, you may determine method names, arguments types as well as return types, but your methods will have no body because that is left to the imagination of those classes that implement a particular interface. A class is said to conform to an interface when it uses the special implements keyword in relation to a specific interface:-

class Account implements Transaction { ... }

Once you implement an interface in a class, it becomes usable as you would any other class. The methods you declare in an interface must be implemented in your inheriting classes and must be declared public. Interfaces may also include fields which are implicitly static and final. For instance, you could create different types of bank accounts to conform to a Transaction interface in the following way:-

package kingsley.java

interface Transaction {
     int BALANCE = 500;
     Object transaction(Object input);
}
class CurrentAccount implements Transaction {
     int bal;
     public Object transaction(Object input) {
         this.bal = BALANCE - (int)input;
         return bal;
     }
     public String toString() { return "Current acc"; }
}
class SavingsAccount implements Transaction {
     int bal;
     public Object transaction(Object input) {
         this.bal = BALANCE + (int)input;
         return bal;
     }
     public String toString() { return "Savings acc"; }
}
public class Account {
      public static void payment(Transaction t, Object input) {
         System.out.println(t + " is debited:   " +  t.transaction(input));
     }
     public static void deposit(Transaction t, Object input) {
         System.out.println(t + " is credited:   " +  t.transaction(input));
     }
     public static void main(String[] args) {
         Integer input = new Integer(600);
         deposit(new SavingsAccount(), input);
         payment(new CurrentAccount(), input);
      }
/* Output
Savings acc is debited:  1100
Current acc is credited:   -100
*//

Observe how both implementing classes CurrentAccount and SavingsAccount are automatically upcast in methods within the Account class that accept a Transaction interface. The payment() and deposit() methods utilise what we call a Strategy design pattern and represent an instance of the complete decoupling of interface from implementation. Strategy design pattern allows method to vary independently from the clients that use it. Theoretically, you can adapt any class to use these methods by simply making them conform to the Transaction interface. Interfaces give you a considerable amount of flexibility, and release you from the sort of constraints inherent in using particular data types and their subclasses. Note the BALANCE field in the Transaction interface which is a constant and is implicitly static and final. Finally, note that methods declared in interfaces have no body.

One way to understand the utility of interfaces is to imagine how you might be able to utilise methods that accept interfaces within your own classes or how they might make it possible for others to make use of your own methods without having access to specific knowledge of its implementation, effectively keeping that part hidden. For example, imagine that a part of a program that allowed you to track the business activities of a property management company looked something like this:-

package kingsley.java

public class Activity {
     String name;
     public Property transact(Property input) { return input; }
     public String getName() { return name; }
     public static void main(String[] args) {
         System.out.println(new Sale(2).transact(new Property("1 Easy Street")));
         System.out.println(new Purchase(8).transact(new Property("5 Main Street")));
     }
}
class Property {
     private static long counter;
     private long id = counter++;
     private String address;
     public Property(String address) { this.address = address;  }
     public String toString() { return "Property " + id + " at " + address; }
}
class Sale extends Activity {
     int id;
     String name = "Sale acc";
     public Sale(int i) { this.id = i; }
     public Property transact(Property input) {
          return input; // Dummy transaction
     }
     public String getName() { return name; }
}
class Purchase extends Activity {
     int id;
     String name = "Purchase acc";
     public Purchase(int i) { this.id = i; }
     public Property transact(Property input) { return input; }
     public String getName() { return name; }
}
/* Output
Property 0 at 1 Easy Street
Property 1 at 5 Main Street
*//

The purpose of this example is to demonstrate one of the ways by which one may reuse the previous Transaction interface within a class that was perhaps written by a completely different individual. The Activity class represents certain business transactions that would be relevant to some property development company. On closer examination, you might notice that the Activity class contains some of the same elements as our earlier Transaction interface. This gives us some sign that it might be possible to make use of the Transaction interface to undertake some of those transactions defined as part of the public interface of the Account class and which might be relevant to activities implemented in the Activity class.

One way to do this, would be to make use of the Adapter design pattern to create an adapter that would allow us to quickly add value to our program. An implementation of this idea would look something like this:-

package kingsley.java

public class Activity {
     String name;
     public Property transact(Property input) {
         return input;
      }
     public String getName() {
         return name;
     }
     public static void main(String[] args) {
         Account.deposit(new ActivityAdapter(new Sale(3)), new Property("1 Easy Street"));
         Account.payment(new ActivityAdapter(new Purchase(6)), new Property("5 Main Street"));
     }
}
class ActivityAdapter implements Transaction {
     Activity activity;
     public ActivityAdapter(Activity activity) {
         this.activity = activity;
     }
     public Property transaction(Object input) {
         return activity.transact((Property)input);
     }
     public String toString() {
         return activity.getName();
     }
}
/* Output
Sale acc is credited Property 0 at 1 Easy Street
Purchase acc is debited Property 1 at 5 Main Street
*//

The result has a more intuitive feel to it. Notice how the ActivityAdapter class takes an Activity object in the constructor and produces an object that has the Transaction interface as a part of it. This is the a classic example of the Adapter design pattern without which it would not be possible for Activity and Account classes to work together. Adapter makes it possible for classes with incompatible interfaces to work together

Multiple Inheritance

When you work with classes, you are limited to inheriting from only one base class. Interfaces relax this constraint somewhat by allowing you undertake a kind of multiple inheritance by combining multiple interfaces within a class. To do this, you simply place interface names in sequence following the implements keyword separated by commas:-

package kingsley.java

interface Forward { void drive(); }
interface Stop { void park(); }
interface Speed {
     void turbo();
}
class GearBox { public void move() { } }
class Automatic extends GearBox implements Forward, Stop, Speed {
     public void drive() { System.out.println("drive()"); }
     public void park() {  System.out.println("park()");}
     public void turbo() {  System.out.println("turbo()"); }
     public void move() { System.out.println("move()"); }
}
public class Car  {
     public static void cruise(Forward x) { x.drive(); }
     public static void park(Stop x) { x.park(); }
     public static void race(Speed x) { x.turbo(); }
     public static void move(GearBox x) { x.move(); }
     public static void main(String[] args) {
         Automatic auto = new Automatic();
         cruise(auto); // Interface Forward
         park(auto); // Interface Stop
         race(auto); // Interface Speed
         move(auto); // class GearBox
     }
}
/* Output
drive()
park()
turbo()
move()
*//

In the preceding example, note the way by which the Automatic class implements multiple interfaces but inherits from only one class. Java will not allow you to inherit from more than one class, but you may implement as many interfaces as you need. The value in this example is in how an object of the Automatic class can be upcast to multiple types, as demonstrated in the main(), and hence my earlier reference to the concept of multiple inheritance.

It is also possible to extend interfaces with inheritance in the same way you do classes by including new fields and methods. Unlike classes though, you can combine several interfaces into a new interface with inheritance. The result is a new interface with much of the same characteristics as those of its base interfaces.

package kingsley.java 

interface SuperHero {
     void powers();
}
interface Alien {
     void planet();
}
interface SuperMan extends Alien, SuperHero {
     void xRayVision();

}
interface Human {
     void normal();
}
interface Monster extends Alien {
     void killHuman();
}
class Skrull implements  Monster {
     void shapeShift() {}
     public void killHuman() {}
     public void planet() {}
}
class ClarkKent implements SuperMan, Human {
     public void normal() {}
     public void planet() {}
     public void xRayVision() {}
     public void powers() {}

}
class Hulk implements Human, SuperHero {
     void thunderClap() {};
     public void normal() {}
     public void powers() {}
}
public class Earth {
     static Alien invasion(Monster mt) {
         return mt;
     }
     static SuperMan change(ClarkKent ck) {
         return ck;
     }
     static void battle(SuperHero sh, Monster m) {
         sh.powers();
         m.planet();
         m.killHuman();
     }
     public static void main(String[] args) {
         Skrull ogre = new Skrull();
         invasion(ogre);
         ClarkKent christopherReeve = new ClarkKent();
         SuperMan superMan = change(christopherReeve);
         battle(superMan, ogre);
         battle(new Hulk(), ogre);
     }
}

One may also choose to nest interfaces within classes and other interfaces with very interesting results.
package kingsley.java

class OuterClass {
     interface Inner1 {
         void d();
         interface Inner2 { void d1(); }
     }
     public class InnerClass1 implements Inner1.Inner2 {
         public void d1() { }
     }
     public interface Inner3 {
         void d();
     }
     private interface Inner4 {
         void d();
     }
     private class InnerClass2 implements Inner4 {
         public void d() {}
     }
     public class InnerClass3 implements Inner4 {
         public void d() {}
     }
     public Inner4 getInner4() { return new InnerClass3(); }
     private Inner4 i4;
     public void getInner4(Inner4 in4) {
         i4 = in4;
         i4.d();
     }
}
interface Outer1 {
     void x();
     interface Outer2 {
         void d();
     }
     // Invalid identifier. Can only be public
     // within interface
     //!  private interface Outer3 { }
}
public class NestingInterfaces {
     public class Class1 implements OuterClass.Inner1.Inner2 {
         public void d1() { }
     }
     class Class2 implements OuterClass.Inner3 {
         public void d() {}
     }
     //  private interface not visible
     //  from outside implementing class
     //! class Class3 implements OuterClass.Inner4 {
     //!       public void d() { }
     //! }
     class Class3 implements Outer1 {
         public void x() {}
         class Class4 implements Outer1.Outer2 {
             public void d() {}
         }
     }
     public static void main(String[] args) {
         OuterClass oc = new OuterClass();
         //  Error can't access private interface
         //! OuterClass.Inner4 = oc.getInner4();
         //  Error can't access private interface members
         //! oc.getInner4().d();
         //  To get to a nested private interface
         //  use another OuterClass
         OuterClass oc2 = new OuterClass();
         oc2.getInner4(oc.getInner4());
     }
}

The example demonstrates some of the features available when working with nested interfaces. As you may observe for instance, it is possible to nest interfaces within interfaces which may have public or package access visibility same as non-nested interfaces do. Calling nested interfaces would then be a simple matter of placing a dot next to the surrounding class or interface, before making a call to the nested interface. Matters get a little complicated however with private interfaces, which provide a way to force the definition of methods without adding the benefit of type. In the main(), accessing private nested interface is not quite so straightforward, as attempts to use a returned reference to the private interface are unsuccessful. This is because in the first instance we are not able to access the private interface directly and secondly we can not access a member of that interface.
We however can access the interface by handing the return value to an object that has permission to use it, in this case another OuterClass object.

It is important to note that in implementing an interface, one is not required to implement any interfaces nested within and private interfaces can not be implemented outside of their defining classes.

Factory Methods

Interfaces have the added benefit of allowing you to make multiple implementations using the Factory Method design pattern. The Factory pattern allows you to define an interface for creating an object, but defer instantiation to implementing classes, making it so that your code is completely isolated from the interface implementation. This method is useful when creating a framework to be used by multiple types. Here is an example that uses a ticket factory to create the various types of tickets people might be interested to buy:-

package kingsley.java;

interface Ticket {
     String type();
     int price();
     }
interface TicketFactory { Ticket getTicket(); }

class AdultTicket implements Ticket {
     int PRICE = 500;
     public String type() { return "Adult Ticket"; }
     public int price() {return PRICE;}
}
class AdultTicketFactory implements TicketFactory {
     public Ticket getTicket() { return new AdultTicket(); }
}
class ChildTicket implements Ticket {
     int PRICE = 40;
     public String type() { return "Child Ticket"; }
     public int price() { return PRICE; }
}
class ChildTicketFactory implements TicketFactory {
     public Ticket getTicket() { return new ChildTicket(); }
}
public class Tickets {
     public static void buyTicket(TicketFactory tf) {
         Ticket t = tf.getTicket();
         System.out.println("Paid - " + "1 " + t.type() + " £" +
t.price() + " - Thank you");
     }
     public static void main(String[] args) {
         buyTicket(new AdultTicketFactory());
         buyTicket(new ChildTicketFactory());
     }
}
/* Output
Paid - 1 Adult Ticket £500 - Thank you
Paid - 1 Child Ticket £40 - Thank you
*//

You can see how the Factory method allows you to easily swap implementation without reference to types. Essentially, you can apply this pattern to any other type that implements the required interface without having to worry about how you might call multiple constructors which would otherwise be the case, absent Factory. The Factory method represents an excellent way to write reusable code and is definitely worth the extra effort.

Interfaces are a very useful tool that provide the opportunity to upcast to more than one base type, they can also be used to more effectively hide your implementation but they are not always necessary and sometimes an abstract class would work just as well relieving you of the additional complexity of interfaces.

Abstract classes

Abstract classes are a kind of mid way point between concrete classes and interfaces with the idea being to provide a common interface by which to manipulate a set of classes, this is to say they provide a basic form that establishes the commonality among classes. To create an abstract class, you simply include the abstract keyword like this:-

abstract class SuperHero { ... }

A class that contains one or more abstract methods must be qualified as an abstract class and making a class abstract does not compel you to make all the methods abstract. An abstract method is an incomplete method, having only a declaration and no body.
It is not possible to make an object of an abstract class, however other classes that inherit from an abstract class must provide method definitions for all the methods in the abstract base class. Likewise one may make an abstract class that does not include any abstract methods particularly in instances when you want to prevent instantiation of that class. Check out the following example:-

package kingsley.java;

abstract class SuperHero {
     void name() {}
     abstract void power();
}
class SuperMan extends SuperHero {
     public void power() {
         System.out.println("SuperMan(Heat Vision) ");
     }
     public void name() { System.out.println("Clark Kent"); }
}
class Hulk extends SuperHero {
     public void power() {
         System.out.println("Hulk(Thunderclap) ");
     }
     public void name() { System.out.println("Bruce Banner"); }
}
class SpiderMan extends SuperHero {
     public void power() {
         System.out.println("SpiderMan(Spider sense) ");
     }
     public void name() { System.out.println("Peter Parker"); }
}
class BatMan extends SuperHero {
     public void power() {
         System.out.println("BatMan(none) ");
     }
     public void skill() { System.out.println("BatMan(Law enforcement
techniques) "); }
     public void name() { System.out.println("Bruce Wayne"); }
}
public class Heroes {
     static void change(SuperHero sp) {
         sp.name();
         sp.power();
     }
     static void changeAll(SuperHero[] sh) {
         for(SuperHero sp : sh) {
             change(sp);
         }
     }
     public static void main(String[] args) {
         SuperHero[] heroes = {
                 new SuperMan(),
                 new Hulk(),
                 new SpiderMan(),
                 new BatMan()
         };
         changeAll(heroes);
         new BatMan().skill();
    }
}
/* Output
Clark Kent
SuperMan(Heat Vision)
Bruce Banner
Hulk(Thunderclap)
Peter Parker
SpiderMan(Spider sense)
Bruce Wayne
BatMan(none)
BatMan(Law enforcement techniques)
*//

And there you have it. The SuperHero class is an abstract class whose methods are only partially implemented and not all of which are declared abstract. Further notice how the derived BatMan class extends the interface by adding a new method, which does not effect the operation of other derived classes.

A good way to remember the distinction between abstract classes and interfaces is to think of abstract classes as providing you with a common interface by which one may manipulate several classes, while interfaces on the other hand allow you to manipulate a class in more than one way.