The six roles of the interface

Discover the six uses for Java's interface language feature

1 2 Page 2
Page 2 of 2

Role 4: Serving as constant repositories

Before Java 5 introduced the static imports language feature and enums, interfaces were widely used as constant repositories. A somewhat lazy developer would use an interface instead of a class as the basis for an enumerated type, to avoid having to include a classname prefix when specifying a constant in the class that implements the interface. Consider Listing 6's Directions constant interface.

Listing 6. Directions.java (version 1)

public interface Directions
{
   int NORTH = 0;
   int SOUTH = 1;
   int EAST = 2;
   int WEST = 3;
}

Now, consider Listing 7's Compass class.

Listing 7. Compass.java (version 1)

public class Compass implements Directions
{
   private int curDirection;

   // ... other code

   public void printDirection()
   {
      switch (curDirection)
      {
         case NORTH: System.out.println("North"); break;
         case SOUTH: System.out.println("South"); break;
         case EAST : System.out.println("East"); break;
         case WEST : System.out.println("West"); break;
         default   : System.out.println("Unknown");
      }
   }
}

The problem with the constant interface is that it causes maintenance headaches. To preserve binary compatibility, the class must always implement the interface, even when the class no longer requires the constants. Also, the presence of such constants might confuse the class's users -- perhaps the constants are not intended to be seen outside of the class, but they will be seen because any constants declared in an interface are implicitly public (and static and final).

Constant interfaces are a kludge that can be eliminated by using static imports or enums. For example, consider Listing 8's replacement to the Directions interface:

Listing 8. Directions.java (version 2)

public class Directions
{
   public final static int NORTH = 0;
   public final static int SOUTH = 1;
   public final static int EAST = 2;
   public final static int WEST = 3;
}

Near the top of the source file that declares Compass, you could specify the following static import statement:

import static Directions.*;

and then refer to NORTH and the other constants without having to include a Directions prefix.

For this example, an enum would be a better choice:

public enum Directions { NORTH, SOUTH, EAST, WEST }

Because the switch statement supports enums, you can rewrite Compass, as shown in Listing 9.

Listing 9. Compass.java (version 2)

public class Compass
{
   private Directions curDirection;

   // ... other code

   public void printDirection()
   {
      switch (curDirection)
      {
         case NORTH: System.out.println("North"); break;
         case SOUTH: System.out.println("South"); break;
         case EAST : System.out.println("East"); break;
         case WEST : System.out.println("West"); break;
         default   : System.out.println("Unknown"); // Not necessary for an enum
      }
   }
}

Role 5: Serving as placeholders for utility methods

Along with default methods, Java 8 evolved the interface language feature to support static methods. You can now declare static methods in an interface. This capability opens up all kinds of possibilities, such as that shown in Listing 10.

Listing 10. Fillable.java

public interface Fillable
{
   public void fill(int color);

   public static int rgb(int r, int g, int b)
   {
      return r << 16 | g << 8 | b;
   }
}

Listing 10 declares a Fillable interface for use with graphical objects (such as circles or triangles) that can paint their interiors via the fill() method. For convenience, an rgb() static method is declared in Fillable to conveniently convert red/green/blue color components to a 32-bit integer value, which can be passed to fill(). This static method belongs to Fillable and can be called, as follows:

int rgb = Fillable.rgb(200, 108, 29);

Role 6: Tagging classes for special services

The final role served by interfaces is to tag (or mark) classes for special services. For example, a class implements the java.lang.Cloneable interface to tell the Object.clone() method that it's legal for that method to make a field-for-field copy of that class's instances. Similarly, a class implements the java.io.Serializable interface to indicate that the class's instances can be serialized and deserialized.

Cloneable and Serializable are empty interfaces. They exist solely to identify to the cloning and serialization mechanisms that instances of their implementing classes can be cloned or serialized.

Conclusion

Java newcomers are often puzzled by interfaces, probably because of their multiple roles. Although I think I've covered all of these roles, let me know in the comments if I've missed a role played by Java's interface language feature.

download
Get the source code for this post's applications. Created by Jeff Friesen for JavaWorld

The following software was used to develop the post's code:

  • 64-bit JDK 8u60

The post's code was tested on the following platform(s):

  • JVM on 64-bit Windows 8.1
1 2 Page 2
Page 2 of 2