Learn Java Programming
7.0 Interfaces
The following are the objectives of this module. This module should help understand the following.
- What is an interface – Is interface a TYPE? (Hint: Need to understand the word CONTRACT)
- Why do we need interface?
- How can a Java object exhibit more than one Type – Why does Java Not support Multiple Inheritance
- How interfaces enable collaboration between objects
- What is meant by callback – How interfaces help callback implementations
- How to enable a client – server communication between objects
- what would be the contract to be defined on the Server
- what would be the contract to be defined for the Callback
We know that any object has a state and a behavior [that correspond to properties and methods]. Also every object belongs to a type (which is the class it belongs to)… But sometimes it is necessary for objects to have more than one type:
Take the example of the Student object.
A Student is also a Person is also a HumanBeing
We know that every Person behaves in a similar way (note, not the same, but in a similar way!)
We know every Person talks – but the finer details like the language, the accent – depends on the person.
(Some people talk English, some talk French and some talk Spanish – those who talk English again may have different accents – the British Accent or the American Accent)
BUT, the underlying principle is that every person talks!
So when modeling a person – it is best to lay the contract for the behavior – the actual behaviour changes depending on the person!
How the person talks English (language and accent) depends on the particular person, whether they are from the United Kingdom or from the United States or from Australia
BUT ultimately every average person talks! We could model that behavior using the method
public String speak() {
//communicate what you need!
}
But how the person will speak will also depend on the “type” of the Person
- – Students speak in their youthful way
- – Parents speak more maturely and
- – Elder people speak wisely
But every body speaks!
Modeling the Human Being:
All Human Beings also exhibit certain behavior other than speaking – all Human Beings eat and sleep
This behavior is best modeled as an interface, because how each Person (who is a HumanBeing eats and sleeps is dependent on whether the Person is a Student, or a Parent or an Elderly person
public interface HumanBeing {
public void eat ( Object food);
public void sleep();
}
So whoever be the Person, they have to eat food and sleep.
The interface above thus lays the contract for someone to be called a Human Being. A Human Being needs to eat food and sleep.
Modeling the Person:
Similarly, we defining the contract for a Person as follows:
public interface Person {
public String speak();
public void dressAppropriately();
}
Now since a Student is a Person and also a Human Being, but have different ways of eating food and sleeping, and speaks differently from the other species!
public class Student implements Person, HumanBeing {
private String name;
public void attendCourse() {
//-- go to school and then to college
}
//-- Since Student is a Person
public void speak() {
//-- talk loudly
}
public void dressAppropriately() {
//-- latest fashion statement
}
//-- Since Student is also a HumanBeing
public void eat (Object food) {
//-- eats lots of fast food
}
public void sleep() {
//-- sleep soundly
}
}
When we define a Student object like this:
Student s1 = new Student();
We can say that the TYPE for s is a Student, and also a Person and also a HumanBeing.
The object s1 now exhibits 3 different TYPES - it is a Student, Person and also a HumanBeing
Now a Parent is also a Person and a Human Being, but have different traits or behaviour than a Student
public class Parent implements Person, HumanBeing {
private String name;
public void earnMoney() {
//-- work for family
}
public void speak() {
//-- feels its better to talk slowly!
}
public void dressAppropriately() {
//-- still follow old fashion
//-- hoping the cycle will turnaround again
}
public void eat (Object food) {
//-- packed lunches and meals at home
}
public void sleep() {
//-- sleep soundly, of course
}
}
By defining the interfaces (which are nothing but contracts) we have defined predictability.
Anybody who is a Person knows how to speak well and dress to the occasion – Any object which represents a HumanBeing, better eat and sleep!
Now we can guarantee that every Student object, will have the above behavior of Person as well as HumanBeing.
And we can also guarantee that each Parent object will also be a Human Being!
The most important thing to take away from this discussion is:
- Interface is used to define similar behavior among different Category of Objects – generalities like Eating and Sleeping – Classes that implement this interface get this behavior – What and How (like the what to eat and how to sleep) are the responsibility of the class defining this behavior.
- An interface defines a Contract – any class implementing an interface has to implement the methods as exactly defined in the interface.
- An interface provides a way for a object to exhibit more than one type of behavior
A Student object is a Person who speaks() and dressesAppropriately() and is also a Human Being who eats() and sleeps()
Why do we need interfaces?
We need interfaces when one object has to exhibit more than type. Also interfaces define the contract between similar types.
Interfaces for callbacks: When communicating between two objects, if the interface is known, any object can invoke the service or do a callback based on the public interface, leaving the called-back object to worry about the implementation.
Let us see this with an example:
Let us design a TimeKeeper ( something like an Alarm clock ) – The duty of the TimeKeeper is to notify the clients that some time has expired.
We would have different types of clients [ like a Bus driver or a Train Engine Driver], which would need to to know the time.
So let us first define the TimeKeeper interface:
public interface TimeKeeper {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObservers();
}
The TimeKeeper would have a public method called registerObserver – Any object that needs to have the service of the TimeKeeper would have to call the method registerObserver() on the TimeKeeper.
Anybody who wants to know the time, would need to implement the Observer interface and would have to register themselves with the TimeKeeper by calling the method registerObserver()
public interface Observer {
public void notifyTime(long currentTimeInMillis);
}
Now the TimeKeeper know the list of clients that need to know the time. And it also knows that whosoever is interested in knowing the time, would definitely have a method called
public void notifyTime(long currentTimeInMillis) {
. . .
}
which the TimeManager can call to inform the client that the time has come! The above method on the Observer would be the callback function, which the TimeManager would call, when the particular time has occurred.
Now let us define a class that would like to know the time:
public class Train implements Observer {
long startTime = 0;
public Train() {
System.out.println("Hi - I am the new Express Train");
startTime = System.currentTimeMillis();
}
public void notifyTime(long currentTimeInMillis) {
Date date = new Date(currentTimeInMillis);
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
System.out.println("Express Train - received a notification @ "
+ sdf.format(date));
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR_OF_DAY, 9);
cal.add(Calendar.MINUTE,0);
cal.add(Calendar.SECOND,0);
long trainStartTimeInMillis = cal.getTimeInMillis();
if (currentTimeInMillis < trainStartTimeInMillis) {
System.out.println("Time not up for train to start");
} else if (currentTimeInMillis >= trainStartTimeInMillis + 60000) {
System.out.println("Train is preparing to leave the station");
} else {
System.out.println("Train has already left the station...");
}
}
}
Now let us look at the implementation of the TimeManager
public class StationMaster implements TimeKeeper {
private List observers = new ArrayList();
public void registerObserver(Observer observer) {
this.observers.add(observer);
}
public void removeObserver(Observer observer) {
this.observers.remove(observer);
}
public void notifyObservers() {
int noOfObservers = observers.size();
System.out.println("About to sent notification to ["
+ noOfObservers + "] observers");
for (int i=0; i < noOfObservers; i++) {
Observer observer = (Observer) observers.get(i);
observer.notifyTime(lastCurrentTime);
}
}
}
Now let us see how the pieces fit together:
public static void main(String args[]) {
//-- first create the StationMaster
StationMaster stationMaster = new StationMaster();
Train theBulletTrain = new Train();
//-- the BulletTrain wants to know the time
//-- so registers with the StationMaster
stationMaster.registerObserver(theBulletTrain);
//-- Now every minute, the stationMaster sends the
//-- time to the registered observers
for (int i=0; i < 1440; i++) {
Thread.sleep(60000);
minuteTimeKeeper.notifyObservers();
}
}
Now let us say we need to add a new Bus which needs to start at 12 noon… So we define a Bus class and implement the Observer interface. Since the Bus will need to know the time from the StationMaster, it will need to implement the Observer interface.
The Observer interface has the method that the StationMaster will use to call back the interested parties (objects)
public class Bus implements Observer {
long startTime = 0;
public Bus() {
System.out.println("Hi - I am the new Red Bus in town");
startTime = System.currentTimeMillis();
}
public void notifyTime(long currentTimeInMillis) {
Date date = new Date(currentTimeInMillis);
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
System.out.println("Red Bus - received a notification @ "
+ sdf.format(date));
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR_OF_DAY, 12);
cal.add(Calendar.MINUTE,0);
cal.add(Calendar.SECOND,0);
long trainStartTimeInMillis = cal.getTimeInMillis();
if (currentTimeInMillis < trainStartTimeInMillis) {
System.out.println("Time not up for Bus to start");
} else if (currentTimeInMillis >= trainStartTimeInMillis
+ 60000) {
System.out.println("The Red Bus is preparing to
leave the station");
} else {
System.out.println("Oh.. The Red Bus has already
left the station...");
}
}
}
Now that we have the RedBus, let us instantiate it, and register with the StationMaster
public static void main(String args[]) {
//-- first create the StationMaster
StationMaster stationMaster = new StationMaster();
Train theBulletTrain = new Train();
//-- the BulletTrain wants to know the time
//-- so registers with the StationMaster
stationMaster.registerObserver(theBulletTrain);
Bus theRedBus = new Bus();
//-- the RedBus wants to know the time
//-- so registers with the StationMaster
stationMaster.registerObserver(theRedBus);
//-- Now every minute, the stationMaster sends
//-- the time to the registered observers
for (int i=0; i < 1440; i++) {
Thread.sleep(60000);
minuteTimeKeeper.notifyObservers();
}
}
Now, if you will notice, we added the Bus object to know the time – we did not have to modify the StationMaster class.
We actually decoupled the relationship between the StationMaster (the TimeKeeper) and the Train and the Bus (the clients)
This was because we used
- Interfaces to define the contract for the clients to receive notifications
- Interface to define the callback that the server will do to notify the clients
Summary:
- Interfaces define the contract which objects have to implement – an interface specifies just the method signature
- An object can implement more than one interface
- Interfaces enable callback mechanism on object instances and allow for client server communication.