Interfaces in Java

Learn the difference between classes and interfaces, then get started declaring, implementing, and extending interfaces in your Java programs

1 2 3 Page 3
Page 3 of 3

Second, you can invoke the default implementation of a method that has been overridden or isn't available because of conflicting default implementations in different interfaces. You do this by prefixing keyword super with the name of the interface containing the default method, as shown below.

Listing 17. Accessing default methods with the keyword super

interface A 
{
   default void method()
   {
      System.out.println("A.method() invoked");
   }
}
public class DMDemo implements A
{
   @Override
   public void method()
   {
      System.out.println("DMDemo.method() invoked");
      A.super.method();
   }
   public static void main(String[] args)
   {
      DMDemo dmdemo = new DMDemo();
      dmdemo.method();
   }
}

After compiling Listing 17 (javac DMDemo.java), run the application (java DMDemo) and you'll observe the following output:

DMDemo.method() invoked
A.method() invoked

Finally, you cannot implement any of Object's public non-final methods as default methods in an interface. Brian Goetz has explained the rationale for this restriction in a posting to the Project Lambda mailing list.

Static methods

A static method is a method that's associated with the class in which it's defined. Every object shares the static methods of its class. Java 8 also lets you define static methods in interfaces. An example from Java's standard class library is java.lang.invoke.MethodHandleInfo's static String referenceKindToString(int referenceKind) method.

To understand the usefulness of interface-based static methods, consider a Drawable interface with a draw() method whose int argument contains the drawing color. It's convenient to express this color as red, green, and blue components, so you add an rgb() static method that converts these components to an int. Check out Listing 18.

Listing 18. Declaring a Drawable interface with a static method

public interface Drawable
{
   public void draw(int color);
   public static int rgb(int r, int g, int b)
   {
      return r << 16 | g << 8 | b;
   }
}

Continuing, you create an application consisting of a Circle class that describes a circle via integer-based center coordinates and a radius, and which implements Drawable to draw a circle. You also create an SMDemo class whose main() method runs the application. Listing 19 presents the source code for these classes.

Listing 19. Accessing Drawable's static method

class Circle implements Drawable
{
   private int x, y, r;
   Circle(int x, int y, int r)
   {
      this.x = x;
      this.y = y;
      this.r = r;
   }
   @Override
   public void draw(int color)
   {
      System.out.printf("Drawing circle(%d, %d, %d) in color %x%n", x, y, r, 
                        color);
   }
}
public class SMDemo
{
   public static void main(String[] args)
   {
      Circle circle = new Circle(10, 20, 5);
      circle.draw(Drawable.rgb(0x80, 0x60, 0x40));
   }
}

SMDemo's main() method instantiates Circle and then calls the object's draw() method to draw the circle. Notice that the rgb() method call is prefixed by this static method's interface. You cannot prefix this method with the interface-implementing class (as in Circle.rgb(0x80, 0x60, 0x40)) because the static method belongs to the interface.

Compile Listing 19 and Listing 18 (javac SMDemo.java) and run the application (java SMDemo). You'll observe the following output:

Drawing circle(10, 20, 5) in color 806040

Java 8 introduced interface-based static methods because it's often convenient to associate utility methods with interfaces instead of associating them with utility classes. Doing this makes source code more readable, and ensures that binary compatibility isn't broken.

A word about interface-based applications

Before Java 8, a Java application consisted of at least one class that declared a main() entry-point method. Starting with Java 8, you can accomplish this task with an interface. For example, consider the SayHello application whose source code appears in Listing 20.

Listing 20. Declaring an interface-based SayHello application

public interface SayHello
{
   public static void main(String[] args)
   {
      if (args.length == 0)
         System.out.println("Hello!");
      else
         System.out.printf("Hello %s!%n", args[0]);
   }
}

The code in Listing 20 could be written just the same with an interface or a class. Starting with Java 8, you could compile this code (javac SayHello.java) without error. You would then run the application and observe appropriate output based on whether or not you specified any command-line arguments. However, an interface-based application type isn't fully equivalent to a class-based application type. For example, unlike with a class, you cannot instantiate an interface. As a result, you cannot declare non-static fields in an interface. Regarding fields, you can declare only static constants in an interface: interfaces don't support static fields with mutable state.

For now, it is probably best to avoid creating interface-based applications, because they don't compile with a pre-Java 8 compiler and have the potential to confuse Java beginners. Although this feature works with Oracle's Java 8 reference implementation, it's possible that a future implementation might prevent interface-based applications from running.

Evolving the interface in Java 9: Private methods

Java SE 9 introduced one significant enhancement to interfaces: private methods. A private method is a concrete instance or static method that's defined in an interface and whose method header is prefixed with the private keyword. Private methods were introduced to let an interface's non-abstract methods share code (reducing code duplication).

An abstract example

I've created a short PMDemo application that demonstrates private methods in an abstract manner. See Listing 21 for the application's source code.

Listing 21. Demonstrating an interface's private instance and static methods

interface I
{
   default void a()
   {
      System.out.println("a() called");
      c();
   }

   default void b()
   {
      System.out.println("b() called");
      c();
   }

   private void c()
   {
      System.out.println("c() called");
   }

   static void d()
   {
      System.out.println("d() called");
      e();
   }

   private static void e()
   {
      System.out.println("e() called");
   }
}

public class PMDemo implements I
{
   public static void main(String[] args)
   {
      PMDemo pmdemo = new PMDemo();
      pmdemo.a();
      System.out.println();
      pmdemo.b();
      System.out.println();

      // The following method call will result in a compiler error because c()
      // is private to interface I.

      // pmdemo.c();

      I.d();
   }

   @Override
   public void b()
   {
      System.out.println("overriding b() called");
      I.super.b();
   }
}

Listing 21 declares a private instance method (c()) and a private static method (e()). The former method is executed by default methods a() and b(), and the latter method is executed by static method d().

Compile Listing 21 (javac PMDemo.java) and run the application (java PMDemo). You'll observe the following output:

a() called
c() called

overriding b() called
b() called
c() called

d() called
e() called

A practical example

The former application doesn't show the usefulness of private methods. To address this deficiency, I've created a second application consisting of Sortable, Employee, and SortableDemo classes. Listing 22 presents Sortable.

Listing 22. Describing a sorting capability for objects

public interface Sortable
{
   public int compareTo(Object o);

   static void sort(Sortable[] x, int a, int b)
   {
      int i, j;
      Sortable t;

      // a is the low index of the partition being sorted, whereas b is the high
      // index. When a >= b, either the partition is sorted or this method was
      // called with illegal values for a and b. In either case, no further work 
      // is done on this partition.

      if (a < b)
      {
         // x[a] is the pivot. Set i to pivot's initial position. Set j to the
         // end of the array (plus one because the inner loop decrements j prior
         // to making a comparison).

         i = a;
         j = b + 1;

         // Determine the pivot's final position. Make sure all integers less
         // than the pivot are on the left of that position and all integers
         // greater than the pivot are on the right of that position.

         do
         {
            // i < b serves as a sentinel in the following loop. This sentinel
            // prevents an ArrayIndexOutOfBoundsException when x[a] is larger 
            // than all subsequent data items. x[a] is the sentinel in the 
            // second loop.

            do { i++; } while (x[i].compareTo(x[a]) < 0 && i < b);
            do { j--; } while (x[j].compareTo(x[a]) > 0);

            if (i >= j)
               break;

            swap(x, i, j);
         }
         while (true);

         // Place pivot in appropriate position.

         swap(x, a, j);

         // Sort partition of Sortables less than pivot position.

         sort(x, a, j - 1);

         // Sort partition of Sortables greater than pivot position.

         sort(x, j + 1, b);
      }
   }

   private static void swap(Sortable[] s, int x, int y)
   {
      Sortable temp = s[x];
      s[x] = s[y];
      s[y] = temp;
   }
}

The Sortable interface introduces a sorting capability for objects. The class of those objects that are to be sorted implements Sortable and overrides its abstract compareTo() method. An application calls Sortable.sort() with an array of Sortables to be sorted along with the start and end indexes of the array as arguments. This method performs the sort via the Quicksort algorithm. It depends on a private static swap() method to swap array elements.

Listing 23 presents the source code to an Employee class that implements Sortable.

Listing 23. Describing a sortable employee

public class Employee implements Sortable
{
   private String name;

   public Employee(String name)
   {
      this.name = name;
   }

   @Override
   public int compareTo(Object o)
   {
      Employee e = (Employee) o;
      return name.compareTo(e.getName());
   }

   public String getName()
   {
      return name;
   }
}

Listing 23 overrides compareTo() to compare the current Employee with an Employee argument based on the employee's name. (For brevity, I haven't included any error checking in case a non-Employee object is passed to compareTo().) The compareTo() method relies on the String class's compareTo() method to perform the actual comparison.

Listing 24 presents a SortableDemo class that combines these source files into an application.

Listing 24. Sorting an array of employees

public class SortableDemo
{
   public static void main(String[] args)
   {
      Employee[] employees = 
      { 
         new Employee("Paul"),
         new Employee("John"),
         new Employee("Zenia"),
         new Employee("Rashid"),
         new Employee("Felix")
      };
      Sortable.sort(employees, 0, employees.length - 1);
      for (int i = 0; i < employees.length; i++)
         System.out.println(employees[i].getName());
   }
}

Compile Listings 24, 23, and 22 (javac SortableDemo.java) and run the application (java SortableDemo). You should observe the following output:

Felix
John
Paul
Rashid
Zenia

Conclusion

The interface is a useful language feature that often confuses newcomers to the Java language. In this Java 101 tutorial, I've explained how interfaces differ from classes, and showed you how to declare, implement, and extend interfaces in your Java programs. I also introduced the two new features added to interfaces in Java SE 8, default and static methods, and the single new feature added to interfaces in Java SE 9, private methods.

1 2 3 Page 3
Page 3 of 3