Java 8 Default Methods


Bhaskar S 10/11/2014


Until now one could not add fully implemented method(s) to interfaces. With Java 8 default methods this changes.

Default methods enable one to add new method(s) to previously defined interfaces without breaking existing implementations of those interfaces and ensuring backward compatibility.

We will now demonstrate the use of default method in an interface with an example.

In this hypothetical example, the application will display the contact information in an XML format.

The following is a simple interface for contact:

Listing.1
/*
 * 
 * Name:   Contact
 * 
 * Author: Bhaskar S
 * 
 * Date:   10/11/2014
 * 
 */

package com.polarsparc.java8;

public interface Contact {
    public String getFirstName();
    public String getLastName();
    public String getEmail();
    public String toXML();
}

The following is a simple implementation of the interface for contact:

Listing.2
/*
 * 
 * Name:   MyContact
 * 
 * Author: Bhaskar S
 * 
 * Date:   10/11/2014
 * 
 */

package com.polarsparc.java8;

public class MyContact implements Contact {
    private String xmlFormat = "<contact>"
            + "<first>%s</first>"
            + "<last>%s</last>"
            + "<email>%s</email>"
            + "</contact>";
    
    private String firstName;
    private String lastName;
    private String email;
    
    public MyContact(String firstName, String lastName, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }

    @Override
    public String getFirstName() {
        return this.firstName;
    }

    @Override
    public String getLastName() {
        return this.lastName;
    }

    @Override
    public String getEmail() {
        return this.email;
    }

    @Override
    public String toXML() {
        return String.format(xmlFormat, this.firstName, this.lastName, this.email);
    }
}

The following is a simple test application using the interface and the implementation for contact:

Listing.3
/*
 * 
 * Name:   MyContactTest
 * 
 * Author: Bhaskar S
 * 
 * Date:   10/11/2014
 * 
 */

package com.polarsparc.java8;

public class MyContactTest {
    public static void main(String[] args) {
        Contact c = new MyContact("John", "Doe", "john.doe@earth.com");
        
        System.out.println(c.toXML());
    }
}

Executing the program from Listing.3 will generate the following output:

Output.1

<contact><first>John</first><last>Doe</last><email>john.doe@earth.com</email></contact>

With time the requirements change and now there is a need for the application to display the contact information in a JSON format as well.

The following is the modified simple interface for contact that uses a default method:

Listing.4
/*
 * 
 * Name:   Contact
 * 
 * Author: Bhaskar S
 * 
 * Date:   10/11/2014
 * 
 */

package com.polarsparc.java8;

public interface Contact {
    public String getFirstName();
    public String getLastName();
    public String getEmail();
    public String toXML();
    
    default String toJSON() {
        return String.format("{first:%s, last:%s, email:%s}",
            getFirstName(),
            getLastName(),
            getEmail());
    }
}

Prior to Java 8, one would implement all the methods of an interface in an implementation class. With Java 8, we can add the new capability using the default method without breaking the existing class MyContactTest.

In other words, the application MyContactTest will continue to work without any code changes even though the interface Contact changed.

The following is a newer version of the test application using the interface and the implementation for contact:

Listing.5
/*
 * 
 * Name:   MyContactTest2
 * 
 * Author: Bhaskar S
 * 
 * Date:   10/11/2014
 * 
 */

package com.polarsparc.java8;

public class MyContactTest2 {
    public static void main(String[] args) {
        Contact c = new MyContact("John", "Doe", "john.doe@earth.com");
        
        System.out.println(c.toXML());
        System.out.println(c.toJSON());
    }
}

Executing the program from Listing.5 will generate the following output:

Output.2

<contact><first>John</first><last>Doe</last><email>john.doe@earth.com</email></contact>
{first:John, last:Doe, email:john.doe@earth.com}

The default methods can only access the methods defined in the interface.

This may lead to the question - can one implement the methods from the Object class such as equals(), hashCode(), or toString() ?

The answer is NO !!!

!!! ATTENTION !!!

Trying to implement any of the methods such as equals(), hashCode(), or toString(), will result in the following compiler error:

A default method cannot override a method from java.lang.Object

What happens when a class tries to implement two or more interfaces that have default method(s) with the same name ?

Here is a simple example:

Listing.6
/*
 * 
 * Name:   IntA
 * 
 * Author: Bhaskar S
 * 
 * Date:   10/11/2014
 * 
 */

package com.polarsparc.java8;

public interface IntA {
    public String getA();
    
    default String desc() {
        return "Type " + this.getA();
    }
}
Listing.7
/*
 * 
 * Name:   IntB
 * 
 * Author: Bhaskar S
 * 
 * Date:   10/11/2014
 * 
 */

package com.polarsparc.java8;

public interface IntB {
    public String getB();
    
    default String desc() {
        return "Type " + this.getB();
    }
}
Listing.8
/*
 * 
 * Name:   ClsAB
 * 
 * Author: Bhaskar S
 * 
 * Date:   10/11/2014
 * 
 */

package com.polarsparc.java8;

public class ClsAB implements IntA, IntB {
    @Override
    public String getA() {
        return "A";
    }

    @Override
    public String getB() {
        return "B";
    }
}

The code in Listing.8 will generate a compiler error. The way to fix that is to override and implement the default method desc() in the class ClsAB as shown below:

Listing.9
/*
 * 
 * Name:   ClsAB
 * 
 * Author: Bhaskar S
 * 
 * Date:   10/11/2014
 * 
 */

package com.polarsparc.java8;

public class ClsAB implements IntA, IntB {
    @Override
    public String getA() {
        return "A";
    }

    @Override
    public String getB() {
        return "B";
    }
    
    @Override
    public String desc() {
        return "Type " + this.getA() + this.getB();
    }
}

!!! ATTENTION !!!

When a class tries to implement two or more interfaces that have default method(s) with the same name, it will result in the following compiler error:

Duplicate default methods named desc with the parameters () and () are inherited from the types IntB and IntA