Java interfaces: what is an interface and when are they used?
In Java, an interface is a "blueprint" that defines which methods are common
to some group of classes. We can then write code that operates on instances of that interface without
having to know the specific class in question. In other words, the purpose of interfaces is generally to
allow us to write more flexible code. By using interfaces, we can avoid duplication or having to write methods that would otherwise
only work on specific classes of objects.
For example, one common example of an interface is the List interface (defined in the standard
java.util package). An interface is declared in a similar way to a class (in fact, it is very
similar to an abstract class), but generally includes a list of method signatures without
implementations (in other words, abstract methods). In addition, all methods in an interface
are assumed to be public. Part of the definition of the List interface looks as follows:
public interface List<T> {
int size();
boolean contains(Object o);
}
Any number of other classes can be defined to implement List. For example,
the Java classes LinkedList, ArrayList, Stack and CopyOnWriteArrayList
all implement List. Because these classes are defined as implementing List, this means in
effect that they "promise" that they have definitions of size() and contains() (and other
methods of the List interface).
Creating an object that implements a given interface
To create an object that implements a given interface, we essentially have two choices:
- if there is already a class that implements that interface that want to use, then we specify what actual class of object we are creating;
- we can also create an anonymous implementation of an interface, in which we use new plus the interface name;
in this case, we must also provide implementations of the methods.
As an example of the first option, here is how we construct an instance of a List and decide that the actual
type will be an ArrayList. Notice how our reference to the object can still be— and generally should be— the name of the interface:
List<String> stringList = new ArrayList<>();
Anonymous interface implementations
We said that we can also create an anonymous implementation of an interface, by using new with the
interface name and then supplying the missing method definition(s). Common examples of this are with interfaces that
only require one or two methods to be implemented, such as the Runnable interface (used when creating
multiple threads):
Runnable r = new Runnable() {
public void run() {
// Method implementation
}
};
(We can also write this more succinctly using a lambda expression.)
Using interface names with factory methods/pluggable APIs
In some cases, we will actually be forced to refer to objects by their interface names, because
the actual class name is not public. This is typical with "pluggable" APIs: we obtain our object via
a "factory" method, and the factory method declares that it returns some instance of
an interface, but the actual class name is not specified or determined by the API:
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
Here, ThreadMXBean is actually the name of an interface. What specific class of object
is returned by the factory method ould vary from platform to platform and/or from JVM to JVM.
What is the advantage of declaring the variable type to be the name of the interface (List) rather
than the name of the actual class (ArrayList)? The answer is essentially flexibility:
- different classes might have specific functionality that we do or don't need;
referring to objects by the interface name makes it clearer which actual methods or functionality
we really need (in this case, we make it clear that our code just requires the object to be "any type of list",
rather than specifically having to be one stored in an array);
- by referring to objects by their interfaces, we can write more flexible methods that can operate
on different types of object, and thereby avoid code duplication.
Calling methods on an interface
When we write methods, we can declare that they take object types of a given interface rather than the specific
class. So instead of writing this:
public void performOperation(ArrayList<String> list) {
}
we can simply write:
public void performOperation(List<String> list) {
}
Our method is now more flexible: we can have a single method that will work on either, say, an ArrayList or
a LinkedList, rather than having to write separate methods or "convert" one list type into another before
calling the method.
Another common example: Appendable
Another common and useful interface provided by Java is
the Appendable interface, which can be used to avoid having to
write multiple methods to append strings to different types of buffer/stream.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.