Java interfaces – evolution and capabilities

Java interfaces – evolution and capabilities

Interfaces are one of the core features of the Java Language Specification (JLS). Like the Java language itself the interfaces construct evolved tremendously with the passage of time making it both complex and highly potent.
In numbers: The first version of the JLS released in 1996 needed 9 pages to describe interfaces, 23 years and 13 Java versions later (JLS – Java SE 13 Edition) the same chapter spans over 53 pages. That is almost a sixfold increase in description length.

Java interfaces in JDK 1.0

The JLS of the first language version states (my emphasis):

An interface declaration introduces a new reference type whose members are constants and abstract methods. This type has no implementation, but otherwise unrelated classes can implement it by providing implementations for its abstract methods.

[JLS-1], p. 183

And this was about it. All members were constants (implicitly public, static and final, with the explicit specification of these modifiers in code being “strongly discouraged” ([JLS-1], p. 187) and all methods abstract.

Rationale of interfaces

[JLS-1] states further about the rationale of interfaces:

Java programs can use interfaces to make it unnecessary for related classes to share a common abstract superclass or to add methods to Object.

[JLS-1], p. 183

Interestingly, this sentence is also to be found in the subsequent specifications up to this day although the difference between interfaces and abstract classes became very small meanwhile. The real reason for the original design of interfaces is rather evading the so-called diamond problem of languages like C++ which support multiple class inheritance.

As James Gosling and Henry McGilton state in their 1996 whitepaper concerning the Java language environment (bold emphasis is italicized in the original text):

Multiple inheritance–and all the problems it generates–was discarded from Java. The desirable features of multiple inheritance are provided by interfaces–conceptually similar to Objective C protocols.

[JLE96]

Ultimately this decision is a tribute to the general desire for simplicity which dominated the Java design. Elsewhere in the same whitepaper Gosling and McGilton state that

[…] inheriting from interfaces provides a reasonable alternative to multiple inheritance, but this practice should not be seen as a substitute for the more powerful but often confusing practice of inheriting from multiple classes.

[JLE96]

Ironically Java 8 introduces default implementations for interface methods. Welcome back diamond problem.

An example

This is how a JDK 1.0 interface might have looked like:

public interface Interface {
	String CONST = "CONSTANT";
	void doSomething();
}

Between JDK 1.0 and J2SE 5.0

Java evolved quickly, many things happening between its first and fifth versions. Probably the most important change from the point of view of interface usage was the addition of nested classes and interfaces in JDK 1.1. Then J2SE 1.2 introduced the strictfp keyword, one of the lesser known features of Java. The strictfp keyword ensures that all floating-point operations taking place in the block marked with the strictfp modifier are done in accordance with the IEEE 754 specification.

Interfaces are no longer just public and abstract, they can also be protected, private, static and strictfp, with protected and private modifiers being limited to nested interfaces ([JLS-2], p. 200).

Java interfaces in J2SE 5.0

J2SE 5.0, initially named J2SE 1.5, is one of the feature-richest Java releases. The features include:

  • Generics
  • Enumerations
  • Annotations
  • Varargs
  • Static imports
  • and many more.

This release not only adds new capabilities to interfaces like generics or nesting of enumerations, but also extends the meaning of the interface concept itself. Beside the original interfaces and the nested ones, annotations – a very special form of interfaces – were introduced. Annotations will not be dealt with here further, since they deserve an article of their own as they dramatically have changed our way of programming.

An Example

The following small example shows what J2SE 5 interface could do:

private interface A< T > {
	void doSomethingWith(T input);
}

private class Impl< T > implements A< T > {
	@Override
	public void doSomethingWith(T input) {
		// do something fancy here
	}
}

private class Other {}

public void doSomething() {
	A< Other > a = new Impl< Other >();
	a.doSomethingWith(new Other());
}

Java interfaces in more recent Java versions

Major changes in Java SE 8

The next big changes came in Java SE 8. While up until then interface methods could only be public abstract, from Java SE 8 onward they could be declared also as static, strictfp or default. It seems like the initial interfaces where really no substitute for the more powerful multiple class inheritance.

But the introduction of default implementations was logical and necessary. Any change in an existing interface would have triggered changes in all implementing classes. While this is no problem in a small project, it is a very strong limitation for libraries and frameworks. As a workaround, you could insert an abstract class between the interface and the real implementation, which in turn had to extend the abstract class. This offered on the one hand a certain flexibility on the other hand it relieved the implementer of the burden of implementing all methods of the interface. An example of this approach is the WebMvcConfigurerAdapter class of the Spring Framework which was deprecated as soon as the Spring project was migrated to Java 8.

The diamond problem

As hinted above, default methods bring back the diamond problem. In the following example both interfaces InterfaceA and InterfaceB have an identically named method. Class ImplAB which implements both interfaces have to override one of the methods or else a compile-error will be issued.

interface InterfaceA {
	default void doSomething() {
		System.out.println("A: Something fancy");
	}
}
	
interface InterfaceB {
	default void doSomething() {
		System.out.println("B: Something fancy");
	}
}

class ImplAB implements InterfaceA, InterfaceB {
	@Override
	public void doSomething() {
		// Overriding necessary due to ambiguity
	}
}

But the problem is not completely new as a mild form of it existed starting with the first version of Java. This concerned ambiguous inherited fields as [JLS-1] (and all subsequent language specifications) defines:

If two fields with the same name are inherited by an interface because, for example, two of its direct superinterfaces declare fields with that name, then a single ambiguous member results. Any use of this ambiguous member will result in a compile-time error.

[JLS-1], p. 188

Consider the following example:

interface InterfaceA {
	String MEMBER = "MEMBER-A";
}
	
interface InterfaceB {
	String MEMBER = "MEMBER-B";
}
	
class ImplAB implements InterfaceA, InterfaceB{
	public void doSomething() {
		System.out.println(InterfaceA.MEMBER);  // MEMBER-A
		System.out.println(InterfaceB.MEMBER);  // MEMBER-B
		System.out.println(ImplAB.MEMBER);      // Ambiguous
	}
}

The variable MEMBER is ambiguous when referenced through the implementing class or this.

Java interfaces in Java SE 9

Java SE 9 brought the last change so far with respect to interfaces: private methods. And yes, they make sense. If interfaces can contain concrete behavior, they also need to structure it.

The differences between abstract classes and interfaces became minimal. Yes, abstract classes can have protected methods and yes, they can declare constructors and yes, they can have state. Abstract classes still have a certain reason of existence, but they become increasingly awkward as more and more of their capabilities are assumed by interfaces. Who knows, in a few years they might be marked as deprecated.

Usage of Java interfaces

Although there is only one keyword for declaring interfaces (actually there are sort of two if you also count annotations) they can be used in a multitude of ways. Interfaces are an extremely powerful concept of the object-oriented paradigm, so it goes without saying that there are some complex ways for using them. As James Gosling explained in an interview with Bill Venners:

Bill Venners: You once said that the core of object-oriented design is „figuring out what the interfaces are.“ Could you elaborate on that?

James Gosling: The interfaces are the things that connect the pieces. You’ve got a toaster and a power plug in the wall; it’s the way you connect. The core of the object-oriented design thing is to try to figure out what the interfaces should be. The actual technology of object-oriented design in a programming language is how you express those interfaces.

From a developer’s point of view, the art is in figuring out the important things to have in the interface. Actually, the hardest part is figuring out the things you should leave out. Because one of the important aspects to designing interfaces is to make them say just enough so that people who want to use them can use them and do what they need to do — but not so much that it makes it difficult for people to change things.

[VEN99]

But this is a topic to be dealt with in another article.


References

[JLE96] Gosling, James; McGilton, Henry. 1996. Java Language Environment – A White Paper. Retrieved on 28.01.2020 from https://www.oracle.com/technetwork/java/langenv-140151.html

[JLS-1] Gosling, James; Joy, Bill; Steele, Guy. 1996. The Java™ Language Specification. Retrieved on 28.01.2020 from http://titanium.cs.berkeley.edu/doc/java-langspec-1.0.pdf

[JLS-2] Gosling, James; Joy, Bill; Steele, Guy; Bracha, Gilad. 2000. The Java™ Language Specification. Second Edition. Retrieved on 28.01.2020 from https://books.google.de/books?id=Ww1B9O_yVGsC

[JLS-13] Gosling, James; Joy, Bill; Steele, Guy; Bracha, Gilad; Buckley, Alex; Smith, David. 2019. The Java® Language Specification Java SE 13 Edition. Retrieved on 28.01.2020 from https://docs.oracle.com/javase/specs/jls/se13/jls13.pdf

[VEN99] Venners, Bill. 1999. James Gosling on Java, May 1999. A Converstion with Java’s Creator, James Gosling. Retrieved on 28.01.2020 from https://www.artima.com/intv/gosling1.html

Petre Sora

Petre Soras Interessen sind vielfältig und befinden sich an der Schnittstelle zwischen Mensch und Informationstechnologie. Als studierter Psychologe und Software Engineer war er knappe sechs Jahre als Java-Entwickler in mehreren Unternehmen tätig. Mit der Gründung der Rezensionsplattform nososo hat er sich entschieden eigene Wege zu gehen. Petre ist als Rezensent und Verfasser von Artikeln für nososo tätig.

Schreibe einen Kommentar