Chapter: Java The Complete Reference - The Java Language - Packages and Interfaces

| Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail |

Default Java Interface Methods

As explained earlier, prior to JDK 8, an interface could not define any implementation whatsoever.

Default Interface Methods

 

As explained earlier, prior to JDK 8, an interface could not define any implementation whatsoever. This meant that for all previous versions of Java, the methods specified by an interface were abstract, containing no body. This is the traditional form of an interface and is the type of interface that the preceding discussions have used. The release of JDK 8 has changed this by adding a new capability to interface called the default method. A default method lets you define a default implementation for an interface method. In other words, by use of a default method, it is now possible for an interface method to provide a body, rather than being abstract. During its development, the default method was also referred to as an extension method, and you will likely see both terms used.

 

A primary motivation for the default method was to provide a means by which interfaces could be expanded without breaking existing code. Recall that there must be implementations for all methods defined by an interface. In the past, if a new method were added to a popular, widely used interface, then the addition of that method would break existing code because no implementation would be found for that new method. The default method solves this problem by supplying an implementation that will be used if no other implementation is explicitly provided. Thus, the addition of a default method will not cause preexisting code to break.

 

Another motivation for the default method was the desire to specify methods in an interface that are, essentially, optional, depending on how the interface is used. For example, an interface might define a group of methods that act on a sequence of elements. One of these methods might be called remove( ), and its purpose is to remove an element from the sequence. However, if the interface is intended to support both modifiable and nonmodifiable sequences, then remove( ) is essentially optional because it won’t be used by nonmodifiable sequences. In the past, a class that implemented a nonmodifiable sequence would have had to define an empty implementation of remove( ), even though it was not needed. Today, a default implementation for remove( ) can be specified in the interface that does nothing (or throws an exception). Providing this default prevents a class used for nonmodifiable sequences from having to define its own, placeholder version of remove( ). Thus, by providing a default, the interface makes the implementation of remove( ) by a class optional.

 

 

It is important to point out that the addition of default methods does not change a key aspect of interface: its inability to maintain state information. An interface still cannot have instance variables, for example. Thus, the defining difference between an interface and a class is that a class can maintain state information, but an interface cannot. Furthermore, it is still not possible to create an instance of an interface by itself. It must be implemented by a class. Therefore, even though, beginning with JDK 8, an interface can define default methods, the interface must still be implemented by a class if an instance is to be created.

One last point: As a general rule, default methods constitute a special-purpose feature. Interfaces that you create will still be used primarily to specify what and not how. However, the inclusion of the default method gives you added flexibility.

 

Default Method Fundamentals

 

An interface default method is defined similar to the way a method is defined by a class. The primary difference is that the declaration is preceded by the keyword default. For example, consider this simple interface:

 

public interface MyIF {

 

     This is a "normal" interface method declaration.

 

     It does NOT define a default implementation. 

int getNumber();

 

     This is a default method. Notice that it provides      a default implementation.

 

default String getString() { return "Default String";

 

}

 

}

 

MyIF declares two methods. The first, getNumber( ), is a standard interface method declaration. It defines no implementation whatsoever. The second method is getString( ), and it does include a default implementation. In this case, it simply returns the string "Default String". Pay special attention to the way getString( ) is declared. Its declaration is preceded by the default modifier. This syntax can be generalized. To define a default method, precede its declaration with default.

Because getString( ) includes a default implementation, it is not necessary for an implementing class to override it. In other words, if an implementing class does not provide its own implementation, the default is used. For example, the MyIFImp class shown next is perfectly valid:

 

 

// Implement MyIF.

 

class MyIFImp implements MyIF {

 

     Only getNumber() defined by MyIF needs to be implemented.

 

     getString() can be allowed to default.

 

public int getNumber() { return 100;

 

}

 

}

 

The following code creates an instance of MyIFImp and uses it to call both

getNumber( ) and getString( ).

// Use the default method.

class DefaultMethodDemo {

public static void main(String args[]) {

 

MyIFImp obj = new MyIFImp();

 

     Can call getNumber(), because it is explicitly

 

     implemented by MyIFImp: 

System.out.println(obj.getNumber());

 

     Can also call getString(), because of default      implementation:

 

System.out.println(obj.getString());

 

}

 

}

The output is shown here:

 

100

 

Default String

 

As you can see, the default implementation of getString( ) was automatically used. It was not necessary for MyIFImp to define it. Thus, for getString( ), implementation by a class is optional. (Of course, its implementation by a class will be required if the class uses getString( ) for some purpose beyond that supported by its default.)

 

It is both possible and common for an implementing class to define its own implementation of a default method. For example, MyIFImp2 overrides getString( ):

 

class MyIFImp2 implements MyIF {

 

// Here, implementations for both getNumber( ) and getString( ) are provided. 


public int getNumber() {

 

return 100;

 

}

 

public String getString() {

 

return "This is a different string.";

 

}

 

}

 

Now, when getString( ) is called, a different string is returned.

 

A More Practical Example

 

Although the preceding shows the mechanics of using default methods, it doesn’t illustrate their usefulness in a more practical setting. To do this, let’s once again return to the IntStack interface shown earlier in this chapter. For the sake of discussion, assume that IntStack is widely used and many programs rely on it. Further assume that we now want to add a method to IntStack that clears the stack, enabling the stack to be re-used. Thus, we want to evolve the IntStack interface so that it defines new functionality, but we don’t want to break any preexisting code. In the past, this would be impossible, but with the inclusion of default methods, it is now easy to do. For example, the IntStack interface can be enhanced like this:

 

interface IntStack {

 

void push(int item); // store an item int pop(); // retrieve an item

 

     Because clear( ) has a default, it need not be

 

     implemented by a preexisting class that uses IntStack. 


default void clear() {

 

System.out.println("clear() not implemented.");

 

}

 

}

 

Here, the default behavior of clear( ) simply displays a message indicating that it is not implemented. This is acceptable because no preexisting class that implements IntStack would ever call clear( ) because it was not defined by the earlier version of IntStack.

However, clear( ) can be implemented by a new class that implements IntStack. Furthermore, clear( ) needs to be defined by a new implementation only if it is used. Thus, the default method gives you

 

        a way to gracefully evolve interfaces over time, and

 

        a way to provide optional functionality without requiring that a class provide a placeholder implementation when that functionality is not needed.

 

One other point: In real-world code, clear( ) would have thrown an exception, rather than displaying an error message. Exceptions are described in the next chapter. After working through that material, you might want to try modifying clear( ) so that its default implementation throws an UnsupportedOperationException.

 

Multiple Inheritance Issues

 

As explained earlier in this book, Java does not support the multiple inheritance of classes. Now that an interface can include default methods, you might be wondering if an interface can provide a way around this restriction. The answer is, essentially, no. Recall that there is still a key difference between a class and an interface: a class can maintain state information (especially through the use of instance variables), but an interface cannot.

The preceding notwithstanding, default methods do offer a bit of what one would normally associate with the concept of multiple inheritance. For example, you might have a class that implements two interfaces. If each of these interfaces provides default methods, then some behavior is inherited from both. Thus, to a limited extent, default methods do support multiple inheritance of behavior. As you might guess, in such a situation, it is possible that a name conflict will occur.

 

For example, assume that two interfaces called Alpha and Beta are implemented by a class called MyClass. What happens if both Alpha and Beta provide a method called reset( ) for which both declare a default implementation? Is the version by Alpha or the version by Beta used by MyClass? Or, consider a situation in which Beta extends Alpha. Which version of the default method is used? Or, what if MyClass provides its own implementation of the method? To handle these and other similar types of situations, Java defines a set of rules that resolves such conflicts.

First, in all cases, a class implementation takes priority over an interface default implementation. Thus, if MyClass provides an override of the reset( ) default method, MyClass’ version is used. This is the case even if MyClass implements both Alpha and Beta. In this case, both defaults are overridden by MyClass’ implementation.

Second, in cases in which a class implements two interfaces that both have the same default method, but the class does not override that method, then an error will result. Continuing with the example, if MyClass implements both Alpha and Beta, but does not override reset( ), then an error will occur.

In cases in which one interface inherits another, with both defining a common default method, the inheriting interface’s version of the method takes precedence. Therefore, continuing the example, if Beta extends Alpha, then Beta’s version of reset( ) will be used.

It is possible to explicitly refer to a default implementation in an inherited interface by using a new form of super. Its general form is shown here:

 

InterfaceName.super.methodName( )

 

For example, if Beta wants to refer to Alpha’s default for reset( ), it can use this statement:

 

Alpha.super.reset();


Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail


Copyright © 2018-2020 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.