CHAPTER 10
Interface is quite similar to Class, but it doesn't provide any storage and serves more as a calling protocol between runtime objects. An interface defines a set of methods, and a class implementing this interface is said to observe the protocol. This allows the object of that class to be assignable to a variable of the interface type, so that the user may program only against the interface without seeking to know the underlying implementation.
The interface definition looks pretty much like class definition, except it can contain only one member type - methods. In addition, all the methods must be abstract. The use of abstract
keyword is optional.
interface Alive {
void walk();
string speak();
string speak(int count); // Support method overloading
}
Interface can be extended by other interfaces. One interface can also extend multiple interfaces.
interface Operable {
void operate();
}
interface Monitorable {
string getStatus();
}
interface ProductionCompliant : Operable, Monitorable {
int getYear();
}
In the example above, ProductionCompliant
extends two interfaces, and also defines a new method. A class that implements this interface thus must implement all of three methods.
To implement an interface, simply put it along with parent type in class definition. A class can implement multiple interfaces.
class Machine {
}
class Car : Machine, ProductionCompliant {
void operate(){ }
string getStatus(){ return ... }
int getYear() { return ... }
}
It's fine that the parent class also implements the same interface or part of it. The dynamic dispatching means we will be always calling the method at the end of inheritance chain. It's not allowed, however, to leave some methods defined by the interface unimplemented, unless the class is abstract.
The polymorphism has its best use for interface-based programming.
interface Talkable {
void call();
}
interface Runnable {
void run();
}
class Person implements Talkable, Runnable {
... ... // Implementation
}
class Dog implements Runnable {
... ... // Implementation
}
void converse(Talkable t1, Talkable t2){ }
bool compete(Runnable r1, Runnable r2){ }
Person p1 = new Person();
Person p2 = new Person();
Dog d1 = new Dog();
converse(p1, p2); // The arguments are treated only as Talkable
compete(p1, d1); // The arguments are treated only as Runnable
In the following example, both the API user and the implementors can focus entirely on the calling protocol as defined by the interface, without knowing the other party. As a result, the component decoupling is achieved.
// interface.jul
module Interfaces;
interface Callable {
void call();
}
// implementation.jul
module Implementation;
import Interfaces;
class CommonCallable implements Callable {
void call() { }
}
class PremiumCallable implements Callable {
void call() { }
}
class UltimateCallable implements Callable {
void call() { }
}
class Factory {
static Callable getCallable(int cost){
... ...
}
}
// main.jul
import Interfaces;
Callable callable = Factory.getCallable(arg);
callable.call();