Lesson Weekend

An interface is a group of methods multiple different classes may inherit.

Demonstration

For instance, say we had an interface called Noisy:

public interface Noisy {
    void angryNoise();
    void happyNoise();  
}

The Noisy interface declared two methods: One for making a happy noise, and one for making an angry noise. Notice it doesn't say how that method should look, or what functionality it contains, it just says "Hey, this functionality should exist.

Now, we know many things are capable of being noisy. So, many classes can implement this interface. For instance, an Elephant could be pretty noisy:

public class Elephant implements Noisy {

    @Override
    public void angryNoise() {
         System.out.println("rumble");
    }

    @Override
    public void happyNoise() {
         System.out.println("trumpet");
    }
}

And a Dog can be pretty loud, too:

public class Dog implements Noisy {

    @Override
    public void angryNoise() {
         System.out.println("growl");
    }

    @Override
    public void happyNoise() {
         System.out.println("bark");
    }
}

However, despite both Dog and Elephant classes implementing the same interface with the same methods, Dogs and Elephants make different noises; so the interface's methods are personalized to each animal's class. This doesn't make the code shorter, per se, but it clarifies that each of these classes MUST provide this functionality.

When we say, a class "implements an interface", we are saying "this class needs to provide the methods of the interface that it implements."

Interfaces allow us to separate what a class should be able to do in terms of functionality (make angry noises, for instance) from how it does it (either rumbling or growling, depending on the animal). Notice that the interface itself does not contain any logic in any of the methods it outlines. It only lists the method signature (what was that again? Look it up!) of each, which means that it declares how many arguments the method should take and of which type they should be.

Interfaces are commonly likened to "contracts" that the developer "signs". Because whenever a class implements an interface it is obligated to include every method outlined in the interface. If it does not, the class won't even compile. Therefore, implementing an interface is like signing a contract saying you promise this class will contain every method listed in the interface.

This helps us keep our code organized, by introducing a check-and-balance system. If I know I am writing a class that should be able to a.) make an angry noise and b.) make a happy noise, I can tell my class that I am implementing the Noisy interface. If I later forget to write those methods, or write them with a different number of type of arguments, I will receive a compiler error.

Rules for Interfaces

Beyond the requirement to include every method, there are several other rules to consider when creating and using interfaces:

  • Interface methods can only be public (and are by default).
  • Member variables in the interface can only be public, static, and final (and are by default).
  • A class must implement all methods of an interface, unless the class is declared as abstract (we won't get into abstract classes at this point. Just remember that there is one exception to this rule.). That means, using the example above, any other People or Animal classes that implement the interface Noisy must have methods for both happyNoise() and angryNoise() defined in their class.
  • When defining the methods implemented by the interface into a class, you should use the @Override annotation.
    • Note: Often, code is able to compile without this annotation. However, it's considered best practice to include it so other developers can easily see which methods are inherited and which are unique to the class. Additionally, telling the compiler this method is overridden from another source will prompt it to alert you if it doesn't actually see a method of that same signature. This provides yet another layer of error-catching and bug-prevention that makes Java so notoriously stable.
  • Interfaces are fixed. When you change an interface, you have to change every class implementing that interface.
  • Interfaces cannot be instantiated; that is, they cannot create objects. Therefore, they do not contain constructors either.
  • An interface can extend multiple other interfaces.

For more information, read the Oracle documentation on Creating Interfaces.

Benefits of Implementing Interfaces

But why would we want to do this? When we inherit abstract classes we're inheriting fully-functional methods complete with the logic required to execute them. The interfaces just contain empty methods! Well, there's several reasons:

Catching Mistakes

Interfaces can catch errors. Because you are required to override and include every method defined by an interface, this ensures the compiler will catch you if you accidentally forget one.

This may seem silly; but imagine a Java application with hundreds and hundreds of classes. If we know ahead of time that many of them will require the same 7 methods (even if the logic for these methods will differ between classes), we can define an interface outlining these methods. Then, we can inherit it wherever we know these 7 methods will be required.

If we accidentally forget any of them; the compiler will let us know immediately.