Evolution of Interfaces in History of Java
Want to learn more about how interfaces have changed over the course of Java's history? Check out this post on the interface changes over the years.
Join the DZone community and get the full member experience.
Join For FreeThe Java programming language has evolved over a period of more than two decades now. There is no other programming language that has evolved so much over such a long period of time, while also maintaining support for its legacy code-base. Java has always provided backward compatibility to the maximum possible extent while keeping pace with the new features available in most of the current programming languages.
The Java programming language is mainly about developing four kinds of types — classes, interfaces, enums, and annotations. The enums and annotations appeared from Java 5 onwards. In this article, I would like to talk about the evolution of the interface type in the Java programming language.
The interface in Java was initially designed as an abstract type, which could be used for multiple inheritances of types. In Java 1.0, an interface definition could contain only two kinds of members static constants and abstract methods:
public interface SomeInterface {
int SOME_CONSTANT = 35; // variable declaration
int abstractMethod(int x, int y); // method declaration
}
All variable declarations in an interface would always be public static and final and would require an assignment. Along with that, all methods in an interface would be public and abstract.
Having only abstract methods and no implementation for methods in an interface, this allowed it to be used for the multiple inheritances of types, while still avoiding the diamond problem of multiple-inheritance.
A class could inherit abstract methods of the interface or provide an implementation. When the same method signature appears in the super-class and also in an interface it is implementing, then the method is inherited from the super-class and not from the interface.
Java 1.1 introduced the idea of inner classes where a class could be a member of a class. From Java 2 onwards, we had the static nested classes and interfaces, and these could also be used inside an interface. Thus, since Java 2, we had four kinds of members in an interface static constants, abstract methods, nested classes, and nested interfaces:
public interface SomeInterface {
int SOME_CONSTANT = 35; // variable declaration
int abstractMethod(int x, int y); // method declaration
// nested class definition
class NestedClass {
// members of a class
}
// nested interface definition
interface NestedInterface {
// member of an interface
}
}
All nested classes and interfaces, defined in an interface would always be public and static.
Later, Java 5 was released with lots of new features. It introduced the generics and the new enum and annotation types.
The generics introduced in Java 5 are about type parameters. We have the generic classes, generic interfaces, and generic methods, ie. classes, interfaces, and methods, that could be defined with type parameters. So, from Java 5 onwards, we could have six types of members in an interface, using static constants, abstract methods, nested classes, nested interfaces, nested enums, and nested annotations.
// generic interface with one type parameter T
public interface SomeInterface<T> {
int SOME_CONSTANT = 35; // variable declaration
int abstractMethod(int x, int y); // method declaration
T abstractMethodUsingGenericType(T[] array, int i); // method using type parameter
// nested class definition
class NestedClass {
// members of a class
}
// nested interface definition
interface NestedInterface {
// member of an interface
}
// nested enum definition
enum NestedEnum {
OBJECT1,
OBJECT2,
;
// methods, variables and constructors
}
// nested annotation definition
@interface NestedAnnotation {
String attrib1();
}
}
The type parameter, T, in the interface definition could only be used for abstract method's return type and parameters for the abstract method. It cannot be used with the static members. The nested enum and annotation are always public and static.
An important feature of Java has always been its backward compatibility. Even though the language has evolved over the years, great care was taken to support legacy code bases. All newer Java versions have always been able to compile and run older source code — without making any changes to the code. This support often comes at a cost. Interfaces are an example of this. The key feature of an interface is that it can only have abstract methods. To allow for backward compatibility, this behavior cannot be easily changed, making it nearly impossible to enhance the existing interfaces in the API with newer methods. Consider the List<E> interface that has existed since Java 2. It would have been desirable to introduce a sort method for List<E>, but it could not have been added as an abstract method since this would break the code for all existing classes that implement the List interface.
To solve this problem, Java 8 added default methods as members in the interface. This allowed the interface to be enhanced with new methods, providing a default implementation for the new method. Java 8 also allowed interfaces to include static methods. Therefore, from Java 8 onwards, the members of an interface can be static constants, abstract methods, default methods, static methods, nested classes, nested interfaces, nested enums, and nested annotations:
// generic interface with one type parameter T
public interface SomeInterface<T> {
int SOME_CONSTANT = 35; // variable declaration
int abstractMethod(int x, int y); // method declaration
T abstractMethodUsingGenericType(T[] array, int i); // method using type parameter
default int defaultMethod(int x, int y) {
// implementation of method
}
static void main(String[] args) {
// any static method, including main can be included in interface
}
// nested class definition
class NestedClass {
// members of a class
}
// nested interface definition
interface NestedInterface {
// member of an interface
}
// nested enum definition
enum NestedEnum {
OBJECT1,
OBJECT2,
;
// methods, variables and constructors
}
// nested annotation definition
@interface NestedAnnotation {
String attrib1();
}
}
The default and static methods in an interface are always public. Now, since we have default methods, which are implementations, it meant that, in Java, we now also have multiple inheritances of behavior and not just types.
Now, we have the diamond problem of multiple inheritances in Java. Since we can implement behavior via default methods, we can now have a repetitive, common code, which can be duplicated in multiple default methods within the same interface. To avoid this, we would normally break the implementation of a method into smaller methods. And, since these methods may not really be required to be available outside the interface, they should ideally be private. This can now be done with Java 9, which introduced private methods in an interface.
The supported members in an interface from Java 9 onwards are static constants, abstract methods, default methods, static methods, private methods, nested classes, nested interfaces, nested enums, and nested annotations:
// generic interface with one type parameter T
public interface SomeInterface<T> {
int SOME_CONSTANT = 35; // variable declaration
int abstractMethod(int x, int y); // method declaration
T abstractMethodUsingGenericType(T[] array, int i); // method using type parameter
default int defaultMethod(int x, int y) {
// implementation of method
// can call the privateMethod and privateStaticMethod here
}
static void main(String[] args) {
// any static method, including main can be included in interface
// can call privateStatic method here
}
private int privateMethod(int x, int y) {
// private method implementation
}
private static void privateStaticMethod(int x, int y) {
// private method implementation
}
// nested class definition
class NestedClass {
// members of a class
}
// nested interface definition
interface NestedInterface {
// member of an interface
}
// nested enum definition
enum NestedEnum {
OBJECT1,
OBJECT2,
;
// methods, variables and constructors
}
// nested annotation definition
@interface NestedAnnotation {
String attrib1();
}
}
Conclusion
It is interesting to note how the nature of an interface has evolved over the years while maintaining backward compatibility. Before Java 8, a core tenet of an interface was that it can only have public and abstract methods. However, from Java 8, an interface can also have non-abstract methods, and Java 9 onwards it can also have private methods.
Hope you enjoyed this post on the evolution of interfaces in Java! Please let me know what you think in the comments below.
Opinions expressed by DZone contributors are their own.
Comments