Overriding Default Methods – Object-Oriented Programming

Overriding Default Methods – Object-Oriented Programming
Overriding Default Methods

Overriding a default method from an interface does not necessarily imply that a new implementation is being provided. The default method can also be overridden by providing an abstract method declaration, as illustrated by the code below. The default method printSlogan() at (1) in the interface ISlogan is overridden by an abstract method declaration at (2) and (3) in the interface INewSlogan and the abstract class JavaMaster, respectively. This strategy effectively forces the subtypes of the interface INewSlogan and of the abstract class JavaMaster to provide a new concrete implementation for the method, as one would expect for an abstract method.

Click here to view code image

interface ISlogan {
  default void printSlogan() {         // (1) Default method.
    System.out.println(“Happiness is getting certified!”);
  }
}
interface INewSlogan extends ISlogan {
  @Override
  abstract void printSlogan();         // (2) overrides (1) with abstract method.
}
abstract class JavaMaster implements ISlogan {
  @Override
  public abstract void printSlogan();  // (3) overrides (1) with abstract method.
}

Conflict Resolution for Default Methods

Conflicts with multiple inheritance of implementation can arise when default methods are inherited from unrelated interfaces.

Example 5.12 illustrates the case where two interfaces define a default method with the same signature. The default method printSlogan() is declared at (1) and (2) in the interfaces ICheapSlogan and IFunnySlogan, respectively. The two method declarations have the same signature. The interface IAvailableSlogan at (3) tries to extend the two interfaces ICheapSlogan and IFunnySlogan. If this were allowed, the interface IAvailableSlogan would inherit two implementations of a method with the same signature, which of course is not allowed—so the compiler flags it as an error. By the same token, the compiler flags an error at (4), indicating that the abstract class Wholesaler cannot inherit two default methods with the same signature.

A way out of this dilemma is to override the conflicting default methods. The abstract class RetailSeller that implements the interfaces ICheapSlogan and IFunnySlogan overrides the conflicting methods by providing an abstract method declaration of the default method printSlogan() at (5). Similarly, the class NetSeller that implements the interfaces ICheapSlogan and IFunnySlogan overrides the conflicting methods by providing an implementation of the default method printSlogan() at (6).

The upshot of this solution is that clients of the classes RetailSeller and NetSeller now have to deal with the new declarations of the printSlogan() method provided by these classes. One such client is the class MultipleInheritance at (10), which calls the method printSlogan() on an instance of class NetSeller at (11). Not surprisingly, the program output shows that the method in the NetSeller class was executed.

What if the class NetSeller wanted to invoke the default method printSlogan() in the interfaces it implements? The overridden default method can be called by the overriding subtype (in this case, NetSeller) using the keyword super in conjunction with the fully qualified name of the interface and the name of the method, as shown at (8) and (9). This syntax works for calling overridden default methods in the direct superinterface, but not at any higher level in the inheritance hierarchy. The class NetSeller can call only default methods in its direct superinterfaces ICheapSlogan and IFunnySlogan. It would not be possible for the class NetSeller to call any default methods inherited by these superinterfaces, even if they had any.

Example 5.12 Inheriting Default Method Implementations from Superinterfaces

Click here to view code image

// File: MultipleInheritance.java
interface ICheapSlogan {
  default void printSlogan() {          // (1)
    System.out.println(“Override, don’t overload.”);
  }
}
//_______________________________________________________________________________
interface IFunnySlogan {
  default void printSlogan() {          // (2)
    System.out.println(“Catch exceptions, not bugs.”);
  }
}
//_______________________________________________________________________________
interface IAvailableSlogan              // (3) Compile-time error.
          extends ICheapSlogan, IFunnySlogan { }
//_______________________________________________________________________________
abstract class Wholesaler               // (4) Compile-time error.
               implements ICheapSlogan, IFunnySlogan { }
//_______________________________________________________________________________
abstract class RetailSeller implements ICheapSlogan, IFunnySlogan {
  @Override                             // Abstract method.
  public abstract void printSlogan();   // (5) overrides (1) and (2).
}
//_______________________________________________________________________________
class NetSeller implements ICheapSlogan, IFunnySlogan {
  @Override                             // Concrete method.
  public void printSlogan() {           // (6) overrides (1) and (2).
    System.out.println(“Think outside of the class.”);
  }
  public void invokeDirect() {          // (7)
    ICheapSlogan.super.printSlogan();   // (8) calls ICheapSlogan.printSlogan()
    IFunnySlogan.super.printSlogan();   // (9) calls IFunnySlogan.printSlogan()
  }
}
//_______________________________________________________________________________
public class MultipleInheritance {      // (10)
  public static void main(String[] args) {
    NetSeller seller = new NetSeller();
    seller.printSlogan();               // (11)
    seller.invokeDirect();
  }
}

Output from the program:

Click here to view code image

Think outside of the class.
Override, don’t overload.
Catch exceptions, not bugs.

Example 5.13 illustrates the case where a concrete method at (1) in the class Slogan and a default method at (2) in the interface ISlogan have the same signature. The subclass MySlogan at (3) extends the superclass Slogan and implements the super-interface ISlogan. The class MySlogan compiles even though it looks like two implementations of the printSlogan() method are being inherited. In this special case there is no multiple inheritance of implementation, as only the implementation from the superclass Slogan is inherited. The implementation of the default method at (2) is ignored. This is borne out by the program output when the method print-Slogan() is called at (5) on an object of class MySlogan.