Everything you need to know about Interfaces in Java
- Details
- Written by Nam Ha Minh
- Last Updated on 26 September 2019   |   Print Email
Perhaps one of the most confusing concepts for beginners in Java is the concept of interface. At first glance, many see that interfaces increase the amount of code and complexity of a program. Because the program is still able to run with or without interfaces, right?
Did you think so in the beginning time you learn about interfaces in Java?
I didn’t understand the benefits of using interfaces when I started learning Java. Sometimes I thought interfaces are not necessary and redundant.
Over the time, by coding and reading a lot, I have been grasping a strong understanding about interfaces and realizing its importance and roles in designing good software programs.
Therefore, I hope what I am going to share with you today about interfaces would help you save time on understanding this important concept in Java programming.
1. What is an interface in Java?
Simply put, an interface is a collection of methods with empty bodies. Let’s take a look at the Runnable interface in the java.lang package:
public interface Runnable { public abstract void run(); }
This interface declares the run() method which is empty (ends with a semicolon). The run() method specifies a behavior that should be implemented by any class whose instances are intended to be executed by a thread.
Another example: a part of the Collection interface in the java.util package:
public interface Collection { boolean add(Object o); boolean contains(Object o); void clear(); boolean isEmpty(); boolean remove(Object o); int size(); }
You see, this interface defines common behaviors for all kinds of collection such as adding an element, removing an element, checking if there is no element, etc. There is no detailed implementation at all. The concrete collection classes such as ArrayList, HashSet, HashMap,… will provide actual implementation for different kinds of collection.
Of course, we can write a new interface of our own. Here’s an example:
public interface Dog { void bark(); void waveTail(); }
This interface specifies WHAT an object of type Dog can do, but it doesn’t tell HOW to do. The how is left for the concrete classes to implement, such as the Hound class in the following example:
public class Hound implements Dog { void bark() { // a hound barks } void waveTail() { // a houd waves tail } }
To summary, an interface defines common behaviors of a type without providing detailed implementation. It separates the WHAT from the HOW. The WHAT is strict but the HOW varies depending on actual implementations.
2. Why Interfaces?
In general, interfaces facilitate the key concepts in OOP like abstraction, inheritance and polymorphism. In addition, interfaces add flexibility and re-usability for software components.
To understand why interfaces are used, let’s see a real-life scenario.
I’m a technical leader of a team of 5 programmers. When designing a program, I prefer using interfaces to create a contract between me and the programmers. For example, I write this interface:
public interface UserDAO { void create(User user); void delete(User user); Collection<User> list(); User get(String name); }
Then I assign a task for a programmer to implement this interface. He will code the actual implementation that fulfills the contract specified by the interface, e.g. in the UserDAOImpl class:
public class UserDAOImpl implements UserDAO { void create(User user) { // implementation goes here... } // implementation for other methods... }
This way, I specify the WHAT and the programmer implements the HOW. So I can focus on designing the program without caring about the details. Then I write a unit test class (don’t worry if you don’t understand some part of the code which is related to JUnit) like the following code:
public class UserDAOTest { private UserDAO userDao = new UserDAOImpl(); @Test public void testCreate() { User user = new User(); userDao.create(user); } // other test methods... }
This allows me to check the programmer’s work by executing test cases in this class. The main program uses the UserDAO interface like this:
public class Program { private UserDAO userDao = new UserDAOImpl(); void doBusiness() { User user = new User(); userDao.create(user); Collection<User> listUser = userDao.list(); // other processing... } }
In this case, interfaces facilitate the concepts of abstraction and inheritance in OOP: The UserDAO interface abstracts the behaviors of an entity that is responsible for managing users in the system. The concrete implementation - UserDAOImpl class is a subtype of the UserDAO interface.
What if the business logic is changed, and another programmer implements the changes in his own class, say AnotherUserDAOImpl? In this case, we simply change the actual type on the right side of the declaration:
private UserDAO userDao = new AnotherUserDAOImpl();
That’s it, and the rest remains unchanged because the program works with the UserDAO interface. It doesn’t care about the detailed implementation, and when the implementation changes, the code remains unchanged. In this case, interfaces add flexibility for the program.
Now, let’s see how interfaces facilitate the concept of polymorphism in OOP for the example above. Suppose that we declare the User as an interface:
public interface User { String getName(); void setName(String name); String getEmail(); void setEmail(String email); }
And here is a default implementation:
public class DefaultUser implements User { // concrete implementation goes here }
In the main program we can use this class like this:
User newUser = new DefaultUser(); newUser.setName("Nam"); newUser.setEmail("nam@codejava.net"); userDao.create(newUser);
Imagine in the future we need to create a Programmer class which is a subtype of User:
public class Programmer implements User { // behaviors of a User // behaviors of a Programmer }
Then we can use the Programmer class like this:
User coder = new Programmer(); userDao.create(coder);
You see, by making the parameter of the create(User)method as an interface, we can pass any objects whose class implements the User interface into the method, thus facilitates the concept of polymorphism in OOP.
You know, Java doesn’t allow multiple inheritances among classes: A class cannot inherit more than one class. But we can have a class implemented multiple interfaces.
Suppose that we want to sort a collection of DefaultUser objects by name. In this case the DefaultUser class must be of type Comparable in order to compare one user with another. Hence we can update the DefaultUser class like this:
public class DefaultUser implements User, Comparable { // implementation for User // implementation for Comparable }
Here, the DefaultUser class is of two types: User and Comparable. When being used with the UserDAO, it is a User. When being used with a collection e.g. ArrayList, it is a Comparable. This is multiple inheritances with interfaces, which adds flexibility to the program.
By separating the WHAT from the HOW, interfaces can also promote decoupling among components, which increases the reusability of components across applications. We can see many examples right in the JDK API.
Let’s look at the JDBC API which allows Java programs to talk with different database systems from JavaDerby to MySQL to Oracle. Do you notice that the Connection, Statement, PreparedStatement, ResultSet… are all interfaces? Where is the actual implementation?
The implementation is provided by specific JDBC-compliant driver libraries such as Connector J for MySQL, Derby driver for JavaDB, etc. Java programs talk only with the JDBC API without knowing about the detailed implementation, thus decouple Java code from the underlying JDBC driver. This increases the reusability and flexibility of the program. When we want to switch to another database system, simple swapping the JDBC driver JAR file.
Another example of decoupling using interfaces is the Servlet API of the Java EE platform. The HttpServletRequest, HttpServletResponse, HttpSession… are all interfaces. Theirs actual implementation is provided by servlet containers such as Tomcat, JBoss, Wildfly, etc. This makes Java web applications decoupled from the server’s runtime environment.
To summary, interfaces are materials of the Java programming language to facilitate OOP concepts like abstraction, inheritance and polymorphism. In addition, interfaces allow decoupling among software component to promote the flexibility and reusability.
I recommend you to read the section "Program to an Interface, not an Implementation" in page 30in this well know Design Patterns book.
3. HOW Interfaces: 13 Rules about Interfaces in Java
1) Fields in an interface are public, static and final implicitly:
That means the following 2 code snippet has the same meaning:
Implicit:
interface Color { int RED = 0xff0000; }
Explicit:
interface Color { public static final int RED = 0xff0000; }
In other words, you actually declare a constant when you put a field into an interface. So an interface can be used to group some related constants together.
That also means that you cannot declare a field whose access specifier is more restrict than public. For example, the following code won’t compile:
interface Color { private int RED = 0xff0000; }
2) Methods in an interface are public implicitly:
Because the interface defines a contract with the outside world, it’s weird if something in the interface is private. The following example shows two methods are both actually public though they are declared with different access specifier:
interface Dog { void bark(); public void waveTail(); }
3) A class can implement multiple interfaces:
Let’s see an example:
class MyFrame extends JFrame implements Runnable, ActionListener { // implement method from Runnable: public void run() { // code } // implement method from ActionListener public void actionPerformed(ActionEvent evt) { // code } }
Here, the MyFrame class implements the Runnable interface so its instances can be executed by a thread. It also implements the ActionListener interface in order to handle click events of buttons. In this case, the MyFrame class overrides method run() and actionPerformed() from its super interfaces.
4) The overriding methods cannot have more restrict access specifiers:
Because methods in the interface are public implicitly, the overriding methods in the subclass cannot have more strict access specifier. For example:
interface Runnable { void run(); } class Runner implements Runnable { void run() { // cause compile error... } }
The Java compiler will complains: attempting to assign weaker access privileges; was public. The run() method in the interface has public access, whereas its overriding version in the Runner class has package access. Thus the code won’t compile.
5) Non-abstract classes must override all methods declared in the super interfaces:
Let’s see an example:
interface Animal { void eat(); void sleep(); } class Dog implements Animal { public void eat() { // dog eats... } }
This code won’t compile because the Dog class overrides only one method eat() from the Animal interface.
6) Abstract classes are not forced to override all methods from their super interfaces. The first concrete class in the inheritance tree must override all methods:
Let’s see an example:
interface Animal { void eat(); void sleep(); } abstract class Felidae implements Animal { public void sleep() { // sleeping } // this abstract class doesn't override the eat() method } class Cat extends Felidae { public void eat() { // cat eats... } }
Here, the Felidae class is abstract so that it is not forced to override all methods from the Animal interface. However, Cat is a concrete class so it must override the remaining method eat() from the super interface.
7) An interface cannot extend another class
8) An interface can extend from multiple interfaces:
Let’s see an example:
interface Animal { } interface Predator extends Animal { } interface Herbivore extends Animal { } interface Human extends Predator, Herbivore { }
9) An interface can be nested within a class:
For example:
class A { interface B { } }
10) An interface can be nested within another interface:
For example:
interface C { interface D { } }
11) Methods in an interface cannot be static and final
12) Since Java 8, an interface can have default methods and static methods:
Java 8 allows an interface can have methods with implementation called default methods and static methods. For exmple:
public interface XYZ { void boom(); // abstract method static void code1() { // shared code } default void foo() { code1(); // other bar's code } default void bar() { code1(); // other doo's code } }
Learn more: Understand Java Interface Default Methods
13) Functional interface is an interface that has only one method:
For example, the following is a functional interface:
interface Runnable { public void run(); }
Functional interfaces are used in Lambda expressions.
Okay. So far I have covered everything I know about interfaces in Java. If you found I missed something, feel free to contribute.
P.S: To understand interfaces in depth, I recommend you take a look at the section 9 - Interfaces in the Java language specification.
Related Tutorials:
- Understand Classes and Objects in Java
- Understand Java Interface Default Methods
- Understand Inheritance in Java
- Understand Abstraction in Java
- Understand Polymorphism in Java
- 12 Rules and Examples About Inheritance in Java
- 12 Rules of Overriding in Java You Should Know
- Java Access Modifiers Examples: public, protected, private and default
- 10 Java Core Best Practices Every Java Programmer Should Know
Comments