In Java, an interface functions much in the same way as an abstract class, which means that an object can never be created from an interface. As a result of this, an interface doesn’t have constructors, but it does have methods.

Before Java 8, an interface could outline the operations that its implementing class performs, but not how the implementing class should perform this operation (more on this later). Now with Java 8, a developer can do both with an interface.

This is because Java 8 introduced default interface methods. So developers now have the option of using traditional abstract methods, as well as the new concrete methods within an interface. Let's take a closer look.

What Is an Interface?

Before you can truly understand the purpose of an interface and how to use it effectively, you’ll need to understand polymorphism. Polymorphism is a core concept of object-oriented programming, which allows a developer to create generalized and specialized behavior with classes (that, in some instances, are not directly related).

The main benefit of polymorphism is that it facilitates the reuse of code, which is vital in today's software development industry.

Related: What Is Polymorphism? And Why It's Worth Learning

So how does polymorphism relate to a Java Interface? In Java, an interface allows classes that wouldn’t be conventionally related to have access to similar operations. In its simplest form, a Java interface is a template that can be easily utilized by the different classes that implement it. These implementing classes can then transform an interface’s method from its generalized state to a more specialized state to accomplish a specific task.

What Are the Benefits of Using Java 8 Interfaces?

The most obvious benefit of using a Java 8 interface is its new concrete method capability.

Another welcome benefit of Java 8 Interfaces is the ability to add new concrete methods to an existing interface, which has already been implemented, without breaking the program. Before Java 8, if you had a class implementing an interface but not using all its methods, that class would have to be labeled as abstract. Otherwise, the program would break.

When Should You Use an Interface?

Many existing programs can potentially utilize Java 8 interfaces. The function of an interface is to make a programmer’s life easier. Though the same functionality can be achieved without interfaces, using them will make your programs more organized and your development process less time-consuming.

If you’re familiar with inheritance, you should know that it’s also a core concept of object-oriented programming that facilitates generalization. With inheritance, similar classes are grouped into parent-child relationships.

With polymorphism (which is the programming behavior that an interface exhibits), classes that wouldn’t generally have access to the same methods (because of a lack of parent-child relationships), can now be processed polymorphically.

A practical example of using an interface is within a software organization’s accounts department. This department will most likely conduct a set of the same methods (or operations) when creating an employee’s payslip and a customer’s invoice.

These are two classes that wouldn’t be conventionally related, but they can now use some of the same operations thanks to Java 8 interfaces.

Creating an Interface in Java

Using the accounts department scenario above, you can create an interface that deals with payment operations. The purpose of this interface is to aid with the generation of payment reports (in the form of invoices, payslips, and other expenditures).

Creating a Java Interface Example

        //Java interface
public interface Payable {
//abstract method public void paymentAmount();
}

The code above generates a simple Java interface. The interface keyword indicates that Payable is an interface and the paymentAmount() method is an abstract method because it’s unimplemented.

Implementing the Payable Interface Example

        public class Employee implements Payable {
//attributes
private String name; private String position;
//primary constructor
public Employee (String name, String position) {
this.name = name; this.position = position;
}
//payLevel method - takes an employee's position and returns their pay
public double payLevel(String position) {
position.toLowerCase();
if (position == "junior" ) {
return 10.00;
}
if(position == "mid-level") {
return 20.0;
}
if(position == "senior") {
return 30.00;
}
return 0 ;
}

@Override public void paymentAmount() {
//passes the position attribute provided by the user to the payLevel() method above
// store the return from the function call to a double variable called pay
//and print the relevant data to the console
double pay = payLevel(position);
System.out.println(name + " pay for this month is: " + pay);
}
}

To use an interface in a class, you’ll first need to implement that interface using the implement keyword, as you can see in the code above. After implementing a new interface, you should create concrete methods for all the abstract methods in the interface, using the @Override keyword.

Related: Polymorphism in Java: How to Overload or Override Methods

Executing the Program Example

        public class Main {
public static void main(String[] args) {
//Create a new employee pay report
Payable JanesPay = new Employee ("Jane Doe", "mid-level");

//calculate the employee's pay
JanesPay.paymentAmount();
}
}

The program above uses the Employee class to generate a new payslip that utilizes the Payable interface. An object can't be created from an interface, but an object can be declared using an interface, as you can see in the code above.

The Jane’s pay object is created using two attributes—an employee’s name, and the employee’s position in the company. There’s only one primary constructor in the employee class, therefore, every new employee object that's created must have two string attributes.

Using the Jane’s pay object, the program calls the paymentAmount() method in the employee class, which produces the following output in the console:

        Jane Doe pay for this month is: 20.0

Implementing the Payable Interface With Customer Example

        public class Customer implements Payable{
private String customerName;
private String projectType;
public Customer(String customerName, String projectType) {
this.customerName = customerName;
this.projectType = projectType;
}
public double customerInvoice(String projectType) {
projectType.toLowerCase();
if (projectType == "small" ) {
return 10.00;
}
if(projectType == "medium") {
return 20.0;
}
if(projectType == "large") {
return 30.00;
}
return 0 ;
}
@Override public void paymentAmount() {
double total = customerInvoice(projectType);
System.out.println(customerName + " total charge for services provided is: " + total);
}
}

The employee and customer classes are unrelated to each other, but because they each implement the same interface, they now have access to similar operations. Like the employee class, the customer class has to implement the interface and override the abstract paymentAmount() method to function correctly.

Now, the same operations that were conducted on an object of the employee class, can also be conducted on an object of the customer class.

Creating a Customer Object Example

        public class Main {
public static void main(String[] args) {
//Create a new customer invoice report
Payable PaulsInvoice = new Customer("Paul Smith", "large");
//calculate the customer's pay
PaulsInvoice.paymentAmount();
}
}

The code above generates the following output in the console:

        Paul Smith total charge for services provided is: 30.0 
    

Because the interface is a Java 8 interface, you can add default methods to it without breaking the code, as you can see in the example below.

Updating a Java 8 Interface Example

        //Java interface
public interface Payable {
//abstract method
public abstract void paymentAmount();
//concrete method
public default void companyName() {
System.out.println("Software Company");
}
}

Before Java 8, adding the concrete method in the code above to your interface would break the interface. This is because interfaces before Java 8 couldn't have concrete methods. However, if the method was abstract, the interface would remain unaffected. But classes that implemented it before the new method was added would break.

Now because of Java 8, adding concrete methods to an interface will not break classes that have already been implemented. Therefore, objects created from any one of the two implementing classes in this account management example can use the companyName() method without altering their existing code.

Using Java 8 Interface Default Method Example

        public class Main {
public static void main(String[] args) {
//Create a new employee pay report
Payable JanesPay = new Employee("Jane Doe", "mid-level");
//call the default method from the interface
JanesPay.companyName();
//calculate the employee's pay
JanesPay.paymentAmount();
}
}

The code above will produce the following in the console:

        Software Company
Jane Doe pay for this month is: 20.0

Using Generalization to Create Reusable Code

Now you can use similar operations for classes that aren't conventionally related with the help of Java 8 Interfaces. You also have the option and the knowledge to use both concrete and abstract methods in your interfaces.

But for classes that are conventionally related, you can learn how to reuse code with inheritance.