Java 11 Developer Certification - Interfaces
March 17, 2021
What we are covering in this lesson
- What is an Interface
- Creating and Implementing Interface
- Casting in Interface
- Overriding a default method in interface
- 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.