What are default
methods?
Let’s take a look at existing stuff first.
As of Java 7, Java interface declares method/s which define
a contract. Any class that implements the interface must provide an
implementation for each method defined by the interface or inherit the
implementation from a superclass.
Note: These
methods declared in the interface are implicitly public so you need not mention
it in the declaration.
Let’s see an example:
interface Orderable {
int
getProduct( int serialNumber);
String
getProductSpecification();
float
getPrice();
float
getDiscount();
}
Example of class implementing above interface
public class Television implements Orderable {
// Method implementations
}
Ok so far so good but what happens now if you wish to update your interface.
There may be a need to add a method in your existing interface. What effect it
will have?
Yes, you guessed it right. This change will force implementing class
to provide implementation for newly added method too in order to reflect the
new interface contract or else code won’t compile. This situation is painful
especially for library designers as it restricts them from updating the API’s
seamlessly without impacting their clients and Java API creators are one of
them. Imagine if Java decided to add a method in for. e.g, List interface. This
could have broken code of anyone who implemented List interface in their code.
Now here is something good and new for you, Interfaces in
Java 8 can now declare methods with implementation code; yes that’s true. This can
happen in two ways.
First, Java 8
allows static methods inside
interfaces.
Second, Java 8 introduces
a new feature called default methods
that allows to provide a default implementation for methods in an interface.
So what this means? It means that if library designer
decides to add a method in existing interface then can do so by declaring it as
a default method. Existing classes implementing the interface will automatically
inherit the default implementations if they don’t provide one explicitly.
Let’s see now how to declare default methods
interface Orderable {
int
getProduct( int serialNumber);
String
getProductSpecification();
float
getPrice();
float
getDiscount();
default
float getPriceWithDiscount(){
if(getDiscount()==0.0){
return getPrice();
}else{
return (getPrice()-getDiscount());
}
}
They are declared
with the default keyword at the beginning of the method
signature, and they provide
an implementation. This implementation will be inherited by every class
implementing the interface.
Like regular interface methods default methods are also implicitly
public hence there’s no need to specify the public modifier.
Benefits of Default
methods
Backward
compatibility – Default methods allow
us to add new methods to an interface that are automatically available in the
implementations. Thus, there’s no need to modify the implementing
classes. They will inherit the default implementation provided in the
interface.
Evolving APIs – As
a library designer you can now update your interface and APIs without impacting
your clients who are consuming your published API’s. Java 8 creators used it to
their advantage and as you will see Java 8 API extensively uses default
methods.
e.g., Now with Java 8 you can call sort() method
to sort a list. Earlier we were relying upon utility class Collections sort()
method. How? They added a default method in List interface
default void sort(Comparator<? super E> c){
Collections.sort(this, c);
}
Are interfaces like
abstract classes now?
Since now interfaces can have default methods which have
implementation it seems like there is no difference between an interface and an
abstract class. But it’s not true. There is still differences between the two
even though default methods have somewhat narrowed gap between them.
So what’s the difference between an abstract class and an
interface?
First, a class can extend only from one abstract class, but a class can implement multiple
interfaces.
Second, an abstract class can enforce a common state through instance
variables (fields) and constructors.
An interface can’t have instance variables and they can’t have constructors too.
So, there are indeed differences between the two. Both are
used for different purposes and choosing between two really depends on the
scenario context.
Multiple Inheritance in Java
Yes, that’s right. Default
methods enable something that wasn’t possible earlier: multiple inheritance of
behavior. This is the ability of a class to reuse code from multiple places.
As we know a class can
implement multiple interfaces and because interface methods can now have
implementations in Java 8, classes can inherit behavior (implementation code)
from multiple interfaces.
Example: In below
example class Z inherits sayHello() from both X and Y.
public interface X {
default void sayHello() {
System.out.println("Hello from X");
}
}
public interface Y extends X {
default void sayHello() {
System.out.println("Hello from Y");
}
}
public class Z implements X, Y {
public static void main(String[] args) {
new Z().sayHello();
}
}
This multiple inheritance of behavior can create problems too. If a class
implements multiple interfaces and more than one interface defines default
method which have same signature then inherited methods will conflict
with each other (aka diamond problem).
Java compiler resolves possible ambiguities when a class inherits several
default methods with the same signature.
There are three
rules to follow when a class inherits a method with the same signature from multiple
places (such as another class or interface):
1. Classes always
win. A method declaration in the class or a superclass takes priority over any default
method declaration.
E.g., in below example
output should be “Hello from Three” since class Three overrides sayHello() and
as per the rule classes always win. If Three doesn’t override sayHello() it
will be different situation which we will discuss in point 2.
public interface IOne {
default void sayHello() {
System.out.println("Hello from IOne");
}
}
public interface ITwo extends IOne {
default void sayHello() {
System.out.println("Hello from
ITwo");
}
}
public class Three implements IOne {
public void sayHello() {
System.out.println("Hello from
Three");
}
}
public class Four extends Three implements IOne, ITwo {
public static void main(String[] args) {
new Four().sayHello();
}
}
2. If Rule#1 doesn’t
apply then sub-interfaces win: the method with the same signature in the most
specific default-providing interface is selected. (If B extends A, B is more
specific than A).
E.g., In this case
output should be “Hello from ITwo” since ITwo is more specific interface which
has default method and Three doesn’t override sayHello so Rule#1 doesn’t apply
here.
public interface IOne {
default void sayHello() {
System.out.println("Hello from IOne");
}
}
public interface ITwo extends IOne {
default void sayHello() {
System.out.println("Hello from
ITwo");
}
}
public class Three implements IOne {
}
public class Four extends Three implements IOne, ITwo {
public static void main(String[] args) {
new Four().sayHello();
}
}
3. Finally, if the
choice is still ambiguous, the class inheriting from multiple interfaces has to
explicitly select which default method implementation to use by overriding it
and calling the desired method explicitly.
E.g., Below code won’t
compile because there is an ambiguity. Error message is ‘Duplicate default
methods named sayHello with the parameters () and () are inherited from the
types ITwo and IOne’
public interface IOne {
default void sayHello() {
System.out.println("Hello from IOne");
}
}
public interface ITwo {
default void sayHello() {
System.out.println("Hello from
ITwo");
}
}
public class Three implements IOne, ITwo {
public static void main(String[] args) {
new Three().sayHello();
}
}
To resolve it, Three
should override sayHello() and explicitly select which default method
implementation to use. With below code output will be “Hello from IOne”
public class Three implements IOne, ITwo {
public void sayHello() {
IOne.super.sayHello();
}
public static void main(String[] args) {
new Three().sayHello();
}
}
Static Interface Methods
Till now we didn’t
stress enough on addition of static methods to Java interfaces. Let’s now try
to understand why this is done and how this addition benefits us. Before that,
here is an example,
interface Orderable {
//regular
and default methods
static
String getDisclaimer(){
return “My disclaimer”;
}
}
How to invoke above
method?
static method
within an interface is identical to defining one in a class, so we can invoke
in exactly same way as we do in case of class static methods. Only difference
is in case of interface method name would be preceded by interface name.
Orderable.getDisclaimer();
Why?
A common Java pattern
is to define both an interface and a utility companion class defining many
static methods for working with instances of the interface. Collections utility
class is a good example which is there to work with Collection objects.
Now since with Java
8 we can have static methods exist inside interfaces, such utility classes can
be removed and their static methods can be moved inside the interface itself. This
will help increase cohesion in our design as all methods are grouped together
under interface umbrella and placeholder utility classes could be removed.
Conclusion
That’s it for default methods. In this article
we explored default methods, its usage and problem that it solves. Default
methods is good addition to Java. It solves major problem which library
designers and API creators face. Ideally interfaces should be used just to
define the contract but later if situation arises which demands addition of new
methods in existing API default methods would come to rescue and allow new
methods to be added without compromising on backward compatibility.

Comments
Post a Comment