Interface Evolution – Object-Oriented Programming

Interface Evolution – Object-Oriented Programming
Interface Evolution

Augmenting an existing interface with an abstract method will break all classes that implement this interface, as they will no longer implement the old interface. These classes will need to be modified and recompiled in order to work with the augmented interface.

Augmenting an existing interface with a default method does not pose this problem. Classes that implement the old interface will work with the augmented interface without modifying or recompiling them. Thus interfaces can evolve without affecting classes that worked with the old interface. This is also true for static methods (p. 251) added to existing interfaces.

Example 5.13 Inheriting Method Implementations from Supertypes

Click here to view code image

// File: MultipleInheritance2.java
class Slogan {
  public void printSlogan() {                        // (1) Concrete method
    System.out.println(“Superclass wins!”);
  }
}
//_______________________________________________________________________________
interface ISlogan {
  default void printSlogan() {                       // (2) Default method
    System.out.println(“Superinterface wins!”);
  }
}
//_______________________________________________________________________________
class MySlogan extends Slogan implements ISlogan { } // (3)
//_______________________________________________________________________________
public class MultipleInheritance2 {                  // (4)
  public static void main(String[] args) {
    MySlogan slogan = new MySlogan();
    slogan.printSlogan();                            // (5)
  }
}

Output from the program:

Superclass wins!

Static Methods in Interfaces

A common practice in designing APIs has been to provide an interface that classes can implement and a separate utility class providing static methods for common operations on objects of these classes. Typical examples are the java.util.Collection interface and the java.util.Collections utility class (Chapter 15, p. 781). Another example is the Path interface and the Paths utility class in the java.nio.file package (Chapter 21, p. 1285). However, now an interface can also declare static methods, and there is no need for a separate utility class.

Static method declarations in a top-level interface are declared analogous to static method declarations in a class. However, a static method in a top-level interface always has public access, whether the keyword public is specified or not. As with static methods in a class, the keyword static is mandatory; otherwise, the code will not compile. Without the keyword static, the method declaration is identical to that of an instance method, but such instance methods cannot be declared in an interface and the compiler will flag an error.

Click here to view code image

static
return_type method_name
 (
formal_parameter_list
)
throws_clause
 {
implementaion_of_method_body

}

Static methods in an interface differ from those in a class in one important respect: Static methods in an interface cannot be inherited, unlike static methods in classes. This essentially means that such methods cannot be invoked directly by calling the method in subinterfaces or in classes that extend or implement interfaces containing such methods, respectively. A static method can be invoked only by using its qualified name—that is, the name of the interface in which it is declared, together with its simple name, using the dot notation (.).

Example 5.14 illustrates the use of static methods in interfaces. The static method getNumOfCylinders() at (1) is declared in the IMaxEngineSize interface. There are two implementations of the method getEngineSize(), at (2) and (3), in the interface IMaxEngineSize and its subinterface INewEngineSize, respectively. The class CarRace implements the subinterface INewEngineSize.

It is not possible to invoke the static method getNumOfCylinders() directly, as shown at (4). It is also not possible to invoke directly the static method getEngineSize() from either interface, as shown at (6). The respective implementations of the static methods can be invoked only by using their qualified names, as shown at (5), (7), and (8). It does not matter that a static method is redeclared in a subinterface; the static method is not inherited. Each static method declaration in Example 5.14 is a new method.

Example 5.14 Static Methods in Interfaces

Click here to view code image

// File: CarRace.java
import static java.lang.System.out;
interface IMaxEngineSize {
  static int getNumOfCylinders() { return 6; }        // (1) Static method
  static double getEngineSize() { return 1.6; }       // (2) Static method
}
//_______________________________________________________________________________
interface INewEngineSize extends IMaxEngineSize {
  static double getEngineSize() { return 2.4; }       // (3) Static method
}
//_______________________________________________________________________________
public class CarRace implements INewEngineSize {
  public static void main(String[] args) {
//  out.println(“No. of cylinders: ” +
//               getNumOfCylinders());                // (4) Compile-time error.
    out.println(“No. of cylinders: ” +
        IMaxEngineSize.getNumOfCylinders());          // (5)
//  out.println(“Engine size: ” + getEngineSize());   // (6) Compile-time error.
    out.println(“Max engine size: ” + IMaxEngineSize.getEngineSize()); // (7)
    out.println(“New engine size: ” + INewEngineSize.getEngineSize()); // (8)
  }
}

Output from the program: No. of cylinders: 6
Max engine size: 1.6
New engine size: 2.4