Java 11 Developer Certification - Interfaces

March 17, 2021

What we are covering in this lesson

  1. What is an Interface
  2. Creating and Implementing Interface
  3. Casting in Interface
  4. Overriding a default method in interface
  5. Extending an Interface & Ambigous References

What is an Interface

An interface is a way to describe a behaviour or group of behaviours that is shared among disparate types of objects. On a daily basis, we use many of the Java supplied interfaces like java.lang.Comparable, java.io.Serializable, and java.util.List. Any object can implement these interfaces and supply code for the required behaviour, methods, to complete the implementation. Once a class implements an interface, it can be treated as an object with the type of that interface in any method calls, i.e., once we implement Comparable to any of our own classes, we can pass instances of our Comparable classes to any methods, that specify a Comparable parameter type. A class can implement many interfaces.

As we already know, there are 3 types of Inheritance

  • Inheritance of state - state is defined by the class’s static and instance fields.
  • Inheritance of implementation - Implementation is defined by the behaviours of the class, its methods.
  • Inheritance of type - An inherited type in Java can be a class or an interface.

Java is said to support multiple inheritance of type because it allows a class to both extend another class and implement multiple interfaces.

Creating and Implementing Interface

An interface is one of the three reference types supported, along with class and enum. An interface can contain the following elements and all members are implicitly public and cannot be declared with any other access modifier, unless specified below.

  • fields, all variables are public static final implicitly
  • method signatures
  • default methods (permitted as of Java 8) - methods declared with the default modifier
  • Static methods (permitted as of Java 8)
  • Private methods (permitted as of Java 9) - Both static and non-static concrete methods can be private.
  • Nested types.
  • method bodies exist only for default, private, and static methods.

An interface that declares one and only one abstract method is called a functional interface.

Lets quickly look at the differences between Class and Interface.

Feature Class Interface
Inheritance Class can extend only one class Class can implement multiple interfaces, Interface can extend multiple interfaces
Default Access Modifier (no modifier) for members, elements package-private implicitly (cannot declare) public implicitly (redundant to declare)
Support for other modifiers public, protected, private concrete methods must be declared as one of static, private static, private, default
Abstract type support Declare class with abstract modifier. Explicitly Abstract. Always abstract. Implicitly Abstract.
Support for State Instance and class fields are supported and objects can be instantiated which have their own state No support for state except global constants (public static finally), an interface can never be instantiated
Support for the ‘default’ modifier None Permitted for public concrete methods only

At a minimum, an interface can be declared as

inteface MyInterface{}

  • An interface can only be public or package-private.
  • An interface can extend one or more interfaces.
  • An interface does not implement another interface or extend class

Now lets look at some code, we create a new package-private Interface InterfaceExample. We have declared 7 fields in it, all with different modifiers. Then we have the main class Interfacetest All the fields defined in the interface will always be public, static and final.

We have then added some concrete methods in the Interface. We have 2 private methods, 1 of them is static. Another default method which is implicitly public and calls the non-static private method.

Then we have one more static method publicStaticMethod which is implicitly public but calling the static private method.

InterfaceTest then implements InterfaceExample, and some code in the main method which executes the two non-private concrete methods, one through a static reference and one through an instance of an object that implements the interface.

Then we have an abstract methods theImportantMethod, and all abstract methods are implicitly public. This method is overridden in the main class.

package maxCode.online.Interface;

interface InterfaceExample {
	// Define some variables

	// no modifiers at all
	String interfaceName = "InterfaceExample";

	// single modifer
	static String staticInterfaceName = "StaticInterfaceExample";
	public String publicInterfaceName = "PublicInterfaceName";
	final String finalInterfaceName = "FinalInterfaceName";

	// double modifiers
	public final String publicFinalInterfaceName = "PublicFinalInterfaceName";
	static final String staticFinalInterfaceName = "StaticFinalInterfaceName";

	// triple modifiers
	public static final String publicStaticFinalInterfaceName = "PublicStaticFinalInterfaceName";

	// These methods are private because we declared them private.
	private String privateMethod() {
        return "private";
    }

	private static String privateStaticMethod() {
		return "private static";
	}

	// These methods are public implicitly
	default String defaultMethod() {
		// You can call private method from a default method
		return privateMethod() + " then default";
	}

	static String publicStaticMethod() {
		// You can call private static method from public static method
		return InterfaceExample.privateStaticMethod() + " then static";
	}

	// This is the public method that would, in theory, be the method
	// that you want all implementing classes to have in common.
	abstract void theImportantMethod();
}

public class InterfaceTest implements InterfaceExample {
	public static void main(String[] args) {
		// Regardless of how you define it, a variable on an interface is
		// public static final
		System.out.println("All fields on an interface are" + " public static final:");
		System.out.println(InterfaceExample.interfaceName);
		System.out.println(InterfaceExample.staticInterfaceName);
		System.out.println(InterfaceExample.publicInterfaceName);
		System.out.println(InterfaceExample.finalInterfaceName);
		System.out.println(InterfaceExample.publicFinalInterfaceName);
		System.out.println(InterfaceExample.staticFinalInterfaceName);
		System.out.println(InterfaceExample.publicStaticFinalInterfaceName);

		System.out.println("\nExecuting concrete methods on interface");
		// public static method can be accessed from type
		System.out.println(InterfaceExample.publicStaticMethod());

		// default method can be accessed from object which implements type
		InterfaceTest it = new InterfaceTest();
		System.out.println(it.defaultMethod());

		System.out.println("\nExecuting methods using the interface type");
		InterfaceTest anotherIt = new InterfaceTest();
		it.testInterface(anotherIt);

		Object o = anotherIt;
		it.testInterface(o);
	}

	public void theImportantMethod() {
		System.out.println("This is the important method that all objects "
				+ "implementing InterfaceTest must override and implement ");
	}

	// Method that accepts the interface as a parameter
	public void testInterface(InterfaceExample it) {
		System.out.println("Executing testInterface with InterfaceExample");
		it.theImportantMethod();
	}

	// Method that accepts on object as a parameter
	public void testInterface(Object o) {
		System.out.println("Executing testInterface with Object");
		// Using instanceof with an interface
		if (o instanceof InterfaceExample) {

			// Casting using an interface
			InterfaceExample it = (InterfaceExample) o;
			it.theImportantMethod();
		}
	}
}

Output

All fields on an interface are public static final:
InterfaceExample
StaticInterfaceExample
PublicInterfaceName
FinalInterfaceName
PublicFinalInterfaceName
StaticFinalInterfaceName
PublicStaticFinalInterfaceName

Executing concrete methods on interface
private static then static
private then default

Executing methods using the interface type
Executing testInterface with InterfaceExample
This is the important method that all objects implementing InterfaceTest must override and implement
Executing testInterface with Object
This is the important method that all objects implementing InterfaceTest must override and implement

So, this section demonstrated that interfaces give you all the benefits of polymorphism for objects that implement them, but keep you free of having to add behaviour to your business entities, that maybe does not quite fit your model, or are there to facilitate behaviour that’s not specific to your entity’s type.

You can declare methods using interface types, you can cast to interface types, and declare variables with an interface type, including arrays.

Casting in Interface

We have already looked at upcasting and downcasting examples in objects.

Lets look at the below code where we are using cast in interface, as it is slightly different than classes.

package maxCode.online.Interface;

//Very simple interface with one method
interface Laughable {
	void laugh();
}

// A class that implements the interface
class Joke implements Laughable {
	public void laugh() {
		System.out.println("That joke is laughable");
	}
}

// A class that does not implement the interface
class Story {
	public void read() {
		System.out.println("This story is a good read");
	}
}

// A class that extends Story class above and implements
// interface
class FunnyStory extends Story implements Laughable {
	// implements laugh() method from Laughable
	public void laugh() {
		System.out.println("That story is funny");
	}

	// overrides read() method from Story
	public void read() {
		System.out.println("This story is a good giggle");
	}
}

// class Farce will be both a Story and Laughable
class Farce extends Story implements Laughable {
	public void laugh() {
		System.out.println("This story is funny in a farcical way");
	}
}

// The main class
public class InterfaceCast {
	public static void main(String[] args) {
		// Story story = new Story();
		Story story = new Farce();
		FunnyStory funnyStory = new FunnyStory();
		Joke joke = new Joke();

		// call pass through method on different types
		// of objects
		testLaughable(joke);
		testLaughable(funnyStory);

		// call pass through method on different types
		// of objects
		testStory(story);
		testStory(funnyStory);

		// Cast objects and pass to our methods
		testLaughable((Laughable) story);
		// testStory((Story) joke);
	}

	// Pass through method to execute laugh method on any
	// object that implements Laughable
	public static void testLaughable(Laughable l) {
		l.laugh();
	}

	// Pass through method to execute read method on any
	// object 'Is A' Story
	public static void testStory(Story s) {
		s.read();
	}
}
That joke is laughable
That story is funny
This story is a good read
This story is a good giggle
This story is funny in a farcical way

We have an interface Laughable which has one abstract method laugh. One class Joke which implements Laughable and a concrete method laugh.

There is one Story class which doesnt extend any class or implement any interface explicitly. It has only one concrete method read.

Another class, FunnyStory, that extends Story and implements Laughable and so implements the laugh method. It also overrides the read method from Story class.

Then we have the main class InterfaceCast where is the main processing, along with two additional static methods testLaughable to execute the laugh method on the passed Laughable type parameter and testStory to execute the read method on Story type parameter.

Have a special look at these lines

Story story = new Story();
testLaughable((Laughable) story);
// testStory((Story) joke);

The testStory call would throw a compiler error if uncommented, with Inconvertible types cannot cast ‘Joke’ to ‘Story’. This is because Joke has no relation with Story, so the compiler error is very much expected.

Now, variable story is also of type Story, and it doesnt extend any other class or implement any interface. But the line does not give any error. This is because the compiler applies different rules to class casting and interface casting. It may be that Story or a subclass of Story will implement the interface and applying this check will prevent this code from being extensible.

But when we run it, we get a runtime ClassCastException. And so we have the new class Farce which implements Laughable and extends Story as well. Now if we run this, all works fine.

Overriding a default method in interface

We have seen about abstract methods in the past, now we will look at the default methods and overriding default methods.

package maxCode.online.Interface;

//Teachable interface with abstract method teach
//and default method teachTheseLessons
interface Teachable {
	// default method
	default void teachTheseLessons() {
		System.out.println("Everyone should learn art and music");
	}

	void teach();
}

// Trainable interface with abstract method train
// and default method teachTheseLessons
interface Trainable {
	// default method
	default void teachTheseLessons() {
		System.out.println("Train them to do this");
	}

	void train();
}

// Our class implements one of these interfaces for now
public class TestDefaultMethods implements Teachable, Trainable {
	// we implement teach and call the default method
	public void teach() {
		teachTheseLessons();
	}

	// we implement train for later use
	public void train() {
		System.out.println("Everyone can be trained to get up early");
	}

	// default method override
	public void teachTheseLessons() {
		System.out.println("Everyone should learn math and science");
	}

	// main method calls teach and train() methods
	public static void main(String[] args) {
		TestDefaultMethods t = new TestDefaultMethods();
		t.teach();
		t.train();
	}
}
Everyone should learn math and science
Everyone can be trained to get up early

We have a couple of interfaces and both have a default method teachTheseLessons, along with one abstract method in each interface.

Since we have implemented both the interfaces, and the default method name in both these interfaces is the same, we have to override that method in the main class, otherwise we will get a compiler error.

Extending an Interface & Ambigous References

Lets create a new class for this. We have an interface Extendable which has a default method defaultMethod, and an abstract method extend. Another interface SubExtendable that extends the first interface. Then we have the main class which implements the SubExtendable interface. This class implements the extend method, and calls the extend method from main method.

We also have the append method in the SubExtendable interface and so we have to implement that too in the main method.

package maxCode.online.Interface;

//Create an interface with a default method and one abstract method
interface Extendable {
	default void defaultMethod() {
		System.out.println("Extendable: Default method called.");
	}

	void extend();
}

// an interface can extend another interface
interface SubExtendable extends Extendable {
	void append();
}

public class ExtendInterfaceExample implements SubExtendable {
	public static void main(String[] args) {
		ExtendInterfaceExample su = new ExtendInterfaceExample();
		su.extend();
	}

	public void extend() {
		// You can call the interface's default method
		// from the concrete method you create.
		defaultMethod();
		append();
	}

	public void append() {
		System.out.println("Appending functionality ");
	}
}

Output

Extendable: Default method called.
Appending functionality 

The thing to note here is that you can extend an interface and any class that implements the extended interface must implement all abstract methods of both interfaces.

Java allows you to make your class abstract and pass the task of defining the concrete behaviour to subclasses.

So we’re gonna create a new class here and check that functionality.

We have one interface Abstractable with one abstract method makeConcrete. There is one abstract class which implements this interface, but doesnt implement the makeConcrete method. It has one abstract method callConcrete as well.

The main class extends the Abstract class. It does not implement the interface, still since the abstract class implements the interface, this class required to implement both the abstract methods, one from the interface and one from the abstract class.

package maxCode.online.Interface;

//Interface with single abstract method
//remember all non-concrete methods are public and abstract
//implicitly on an interface.
interface Abstractable {
	void makeConcrete();
}

// An abstract class does NOT have to define concrete methods
// for the abstract methods the interface it implements
abstract class AbstractClass implements Abstractable {

	// Create an additional abstract method -
	// Note that this method is NOT public, but package-private
	// and you must declare it abstract.
	abstract void callConcrete();
}

// AbstractableExample extends AbstractClass (which in turn
// implements Abstractable). The AbstractableExample is required
// to implement all abstract methods defined by the interface and
// not implemented by the abstract class as well as any abstract
// methods declared on the abstract class itself.
public class AbstractableExample extends AbstractClass {
	public static void main(String[] args) {
		AbstractableExample e = new AbstractableExample();
		e.callConcrete();
	}

	// Method is required through extension of AbstractClass -
	// defined on the interface AbstractClass implements.
	public void makeConcrete() {
		System.out.println("method declared on Abstractable interface");
	}

	// Method required through extension of AbstractClass
	void callConcrete() {
		System.out.println("method declared on AbstractClass class");
		makeConcrete();
	}
}

Output

method declared on AbstractClass class
method declared on Abstractable interface

We didnt explicitly implement the interface, but we did implement its method since the abstract class implemented the interface and the main class extended the abstract class.

One more thing to remember here is that if the abstract class and interface both have the same method signature, that is absolutely valid. But if the abstract class and interface both have the same variable name used within that method, and another class extend this abstract class and implements the method, the compiler will throw a Ambigous error. So this could result in ambiguous references and method clashes which may occur because of multiple inheritance of type.

Thats all for this section, in the next part we will see the comparision of abstract classes and interfaces in a little more detail.