There are a number of situations in software engineering when it is important for disparate groups of programmers to agree to a "contract" that spells out how their software interacts.
Each group should be able to write their code without any knowledge of how the other group's code is written.
Interfaces are such contracts.
An interface is a reference type, similar to a class, that can only contain
Method bodies exist only for default methods and static methods.
Interfaces cannot be directly instantiated, but they can be
Let us imagine that we want to define a standardized Application Programming Interface (API) for controlling cars for all car manufactures to implement.
We could do that by defining an interface!
Defining an interface is very similar to defining a class.
interface CarController extends VehicleController {
// constant declarations
double MAX_SPEED = 100;
double SAFE_SPEED = 80;
// abstract methods
void accelerate(double targetSpeed);
double getCurrentSpeed();
// default methods
default void evaluateSpeed() {
if(this.getCurrentSpeed()<=SAFE_SPEED)
System.out.println("The car is at a safe speed: "+ getCurrentSpeed()+" km/h");
else
System.out.println("The car is at a risky speed: "+ getCurrentSpeed()+" km/h");
}
// static methods
static void isSpeedAllowed(double speed) {
if(speed <= MAX_SPEED)
System.out.println(speed+" km/h is allowed.");
else
System.out.println(speed+" km/h is not allowed.");
}
}
To use an interface, you write a class that implements the interface.
When a concrete class implements an interface, it provides a method body for each of the abstract methods declared in the interface.
class Fiat500Controller implements CarController {
private double speed = 0;
@Override
public void accelerate(double targetSpeed) {
this.speed = Math.min(targetSpeed, 100);
System.out.println("Accelerating car to " + speed + " km/h.");
}
@Override
public double getCurrentSpeed() {
return this.speed;
}
}
In our example, each automobile manufacturer would implement this interface, possibly one per car model. Fiats's implementation would likely be substantially different from Toyota's, but both manufacturers would still adhere to the same interface.
An interface declaration consists of
public
, private
,
protected
, static
...),interface
,For example:
interface CarController extends VehicleController, SafeController {
// interface body here
}
If you do not specify that an interface is public
, then it is
accessible only to classes defined in the same package as the interface.
An interface can extend other interfaces, just as a class can extend another class. However, whereas a class can extend at most one other class, an interface can extend any number of interfaces.
An interface body can contain the following.
1. Constants are
public
, static
,
and final
.// this:
double SAFE_SPEED = 80;
// is interpreted as this:
public static final double SAFE_SPEED = 80;
2. Abstract methods
public
, and void accelerate(double targetSpeed);
3. Default methods are
default
modifier,public
,default void evaluateSpeed() {
if (this.getCurrentSpeed() <= SAFE_SPEED)
System.out.println("The car is at a safe speed: "+ getCurrentSpeed()+" km/h");
else
System.out.println("The car is at a risky speed: "+ getCurrentSpeed()+" km/h");
}
4. Static methods are
static
modifier,public
, andstatic void isSpeedAllowed(double speed) {
if(speed <= CarController.MAX_SPEED)
System.out.println(speed+" km/h is allowed.");
else
System.out.println(speed+" km/h is not allowed.");
}
Did you note how all constants and methods in an interface are implicitly public
? So you may as well omit the public
modifier in your code.
Drawable
.
void draw()
.Printable
.
void print()
.Clearable
that extends Drawable
.
void clear()
.DrawablePrintableClearable
that extends
Drawable
and Printable
.
void clear()
.You can find the solution to this exercise here.
To declare that a class implements an interface, you include an implements
clause in the class declaration.
public class Car implements Drivable {
// class body here
}
Your class can implement more than one interface, so the implements
keyword is followed by a comma-separated list of the
interfaces implemented by the class.
public class SuperCar implements Drivable, Flyable {
// class body here
}
By convention, the implements
clause follows the extends
clause, if there is one.
public class Car extends Vehicle implements Drivable {
// class body here
}
A concrete class that implements an interface must provide implementations for all abstract methods defined in the interface.
public interface Drivable {
void accelerateTo(double speed);
void breakTo(double speed);
void turnTo(double radius);
}
public class Car implements Drivable {
@Override
public void accelerateTo(double speed) {
// method body
}
@Override
public void breakTo(double speed) {
// method body
}
@Override
public void turn(double radius) {
// method body
}
}
Consider the interface Comparable
, which defines how to compare
the size of objects.
public interface Comparable {
// this (object calling isLargerThan)
// and other must be instances of the same class.
// it returns 1, 0, -1 if
// this is greater than, equal to, or less than other
int isLargerThan(Comparable other);
}
Any class can implement Comparable
if there is some way to compare
the relative "size" of objects that instantiated the class.
If you know that a class implements Comparable
, then you know that
you can compare the size of the objects that instantiate that class.
Here is how a Rectangle
class could implement the Comparable
interface.
public class Rectangle implements Comparable {
public int width = 0;
public int height = 0;
public int getArea() {
return width * height;
}
// the method required to implement
// the Comparable interface
@Override
public int isLargerThan(Comparable other) {
Rectangle otherRect = (Rectangle) other;
if (this.getArea() < otherRect.getArea()) return -1;
else if (this.getArea() > otherRect.getArea()) return 1;
else return 0;
}
}
Hierarchies in Java usually involve interfaces and classes, like the one shown below (from the Collection API).
Implement the interfaces of the previous exercise.
public class Rectangle implements Drawable
public class Circle implements Printable
public class Triangle implements DrawablePrintableClearable
Write a Runner
class for testing.
You can find the solution to this exercise here.
When you define a new interface, you are defining a new reference data type. So, you can use interface names anywhere you can use any other data type name.
If you define a reference variable whose type is an interface, any object you assign to it must be an instance of a class that implements the interface.
Example: You can do
List<String> strings = new ArrayList<String>();
because
ArrayList implements List
Consider the interface below.
public interface DoIt {
void doSomething(int i, double x);
int doSomethingElse(String s);
}
Suppose that, at a later time, you want to add a third method to DoIt
, so that the interface now becomes the following.
public interface DoIt {
void doSomething(int i, double x);
int doSomethingElse(String s);
boolean didItWork(int i, double x, String s);
}
If you make this change, then all classes that implement the old DoIt
interface will break because they no longer implement
the old interface.
Programmers relying on this interface will protest loudly!
This impact is particularly serious if the number of classes that implement DoIt
is large.
If you want to add additional methods to an interface, you have some non-breaking options.
DoItPlus
interface that
extends DoIt
.public interface DoItPlus extends DoIt {
boolean didItWork(int i, double x, String s);
}
public interface DoIt {
void doSomething(int i, double x);
int doSomethingElse(String s);
default boolean didItWork(int i, double x, String s) {
// Method body
}
}
public interface DoIt {
void doSomething(int i, double x);
int doSomethingElse(String s);
static boolean doSomethingStatically() {
// Method body
}
}
Java 8 introduced the “Default Method” feature, which allowed developers to add new methods to the interfaces without breaking the existing implementation of these interface.
It provided flexibility to allow interface defined implementations, which will use as default in the situation where a concrete class fails to provide an implementation for that method.
If
public interface OldInterface {
public void existingMethod();
}
becomes
public interface OldInterface {
public void existingMethod();
default public void newDefaultMethod() {
System.out.println("New default method is added in the interface");
}
}
then the class below will still compile successfully.
public class OldInterfaceImplementation implements OldInterface {
public void existingMethod() {
// existing implementation is here ...
}
}
Then, its instances will have a new method.
OldInterfaceImplementation obj = new oldInterfaceImpl();
// print “New default method added in interface”
obj.newDefaultMethod();
A considerable motivation for default methods was the complexity of reengineering existing JDK frameworks.
Modifying one interface in the JDK framework breaks all classes that extends the interface, which means that adding any new method could break millions of lines of code. Therefore, default methods have been introduced as a mechanism to extending interfaces in a backward compatible way.
Default methods can be provided to an interface without affecting the implemented classes as it already includes an implementation. An implementing class can override the default implementation provided by the interface.
Default methods can be overridden in an implementing class, whereas static methods cannot.
If a static method belongs to an interface, it can only be invoked via the interface itself, not via the class implementing the interface.
public interface MyInterface {
default void defaultMethod(){
System.out.println("Default");
}
static void staticMethod(){
System.out.println("Static");
}
}
public class MyClass implements MyInterface {
public static void main(String[] args) {
MyClass.staticMethod(); //not valid - static method may be invoked on containing interface class only
MyInterface.staticMethod(); //valid
}
}