Say you have a class called MilkGiver, which represents animals that nurse their young (kangaroos, cats, whales, etc.). MilkGiver has fields and methods that encapsulate the act of giving milk. (Please don’t ask me how.)
And you have another class called EggLayer, which is for animals that lay eggs (eagles, crocodiles, etc.). EggLayer has fields and methods that encapsulate the act of laying eggs.
Now you’d like to define a new class that both lays eggs and gives milk by inheriting those abilities:
Well, sorry, Charlie. Java doesn’t allow inheritance from more than one class at a time–what’s called multiple inheritance. Regrettably, you’re going to have to code at least one of the functions (laying eggs or giving milk) all over again within Platypus and inherit from the class that does the other function.
What Java does allow, however, is Interfaces. An Interface is essentially a promise: that a class has the ability to perform a given function.
Rather than saying classes extend interfaces, we say it implements them, like this:
public interface EggLayer {
public static final String ACTIVITY = "Laying eggs";
public void layEggs();
}
public interface MilkGiver {
public static final String ACTIVITY = "Giving milk";
public void giveMilk();
}
public class Platypus implements EggLayer, MilkGiver {
@Override
public void giveMilk() {
System.out.println(MilkGiver.ACTIVITY);
}
@Override
public void layEggs() {
System.out.println(EggLayer.ACTIVITY);
}
}
We can then use Platypus in any of these ways:
Platypus perry = new Platypus();
MilkGiver elsie = new Platypus();
EggLayer camilla = new Platypus();
and elsie would be limited to giving milk, camilla could only lay eggs, and perry can do both. elsie can be defined as a MilkGiver, and camilla as an EggLayer, although they are both of class Platypus, because Platypus implements these interfaces.
(By the way, if necessary we can make elsie lay eggs too by casting:
if (elsie instanceof Platypus) { // This condition is true.
(Platypus) elsie.layEggs();
}
if (elsie instanceof EggLayer) { // This one too. As a Platypus,
(EggLayer) elsie.layEggs(); // elsie is also an EggLayer.
}
Notice how a class can implement more than one interface.
Like classes, interfaces can extend other interfaces. But where a class can extend only one other class, an interface can extend any number of other interfaces:
public interface ChildInterface extends InterfaceA, InterfaceB {
...
}
Limitations on Interfaces
You can’t instantiate an interface, so by definition, all fields in an interface are public, static
, and final
and all methods in an interface are public
and abstract
. (Or are they? See “Interface Methods Are Abstract,” below.)
Furthermore, any class that implements an interface must override all the interface’s methods.
Interfaces are useful not just for providing the template for code; they’re great for providing constants to classes that implement them; you can have an interface that’s nothing but constants if you so choose.
Red and Blue Elephants
When I was a lad, there was a craze for elephant jokes. Example:
Q: Why to elephants wear red tennis shoes?
A: To hide in cherry trees.Q: Have you ever seen an elephant in a cherry tree?
A: No.Q: See–it works!!
The joke that applies to this discussion is this one:
Q: How do you shoot a blue elephant?
A: With a blue elephant gun.Q: How do you shoot a red elephant?
A: Hold its trunk till it turns blue, then shoot it with a blue elephant gun.
One function of interfaces is to make different classes of objects look like another common class so you can write one set of code that can work with all of them.
It’s like holding the red elephant’s trunk till it turns blue. A Tesla, a Model T, a little red wagon, and a Saturn V rocket are the red elephants, all of which can accelerate. You can create a Vehicle interface as the blue elephant. You have no way of shooting the red elephants–i.e., working with the specific vehicle implementations–directly, because your gun–the code you’ll use to manipulate them–only shoots blue elephants. You need to turn your red elephants to blue ones like this:
Typically, you may have a number of POJOs representing data from a number of different sources. Their layouts are dissimilar but they have enough in common that you can write an interface that will describe all of them, then make sure all these classes implement that interface.
To offer an example from my personal experience, there was a database with one set of tables that described doctors and another that described hospitals–the “red elephants.” Both of these have names, addresses, and phone numbers, so we could write an interface–the “blue elephant”–that can be manipulated by a single set of code–the “blue elephant gun.”
Interfaces as Placeholders
One of the most important functions of an interface is that you can compile code that uses it even though the code that implements it isn’t available–in fact, the implementation may not even exist yet. Suppose you had an interface that describes the methods for operating a vehicle:
public interface Vehicle {
public static final String MY_LOCATION = "USA";
public abstract int accelerateTo(int targetMph);
public abstract void brake();
}
(Since all methods in an interface are public and abstract, and all data fields are public, static, and final, the attributes public
, static
, final
, and abstract
are superfluous and can be omitted. I just happen to like to explicitly specify all but abstract
–but hey, that’s just me.)
You can write code that creates a vehicle and makes it accelerate:
Class<?> vehicleClass = Driver.class.getClassLoader()
.loadClass("com.tesla.ModelWhatever");
Constructor<?> vehicleConstructor = vehicleClass.getConstructor();
Vehicle myVehicle = (Vehicle) vehicleConstructor.newInstance();
myVehicle.accelerateTo(target);
myVehicle.brake();
without class com.tesla.ModelWhatever being available–or even its name known at compile time (for example, you might obtain it from a property file or a command line parameter). And the same accelerateTo method would work if, instead of com.tesla.ModelWhatever, the implementation were of class com.toyota.Prius, com.ford.ModelT, com.radioflyer.LittleRedWagon, gov.nasa.SaturnVRocket, or some conveyance that hasn’t even been invented yet–even though, in the spirit of polymorphism and encapsulation, they all accelerate in totally different ways.
Try It!
Here’s an example based on the discussion above. In this example, we’ll load classes by name. Start by downloading project Lesson-03-Exercise-02, then import it to your Eclipse environment.
Try running the Driver class as a Java project. It will fail, but you’ll now have a Run configuration in your IDE. We need to tweak that to run successfully, so here’s how:
- In Lesson-03-Exercise-02 you’ll find a folder named lib, and in it a file named vehicle.jar. Drag this file into your Libs project in the Package Explorer. It doesn’t matter whether you move it or copy it. The idea is to have it in a location separate from the Java project to emphasize that it’s independent.
- Choose Run/Run configurations… from the Eclipse menu. The Run Configurations dialog will open and Driver will be selected in the tree on the left.
- Click on the Dependencies tab. Click on Classpath Entries in the tree, then click the Add JARs… button. The JAR Selection dialog opens. Under Libs, select vehicle.jar and click OK. You’ll now see that JAR in your runtime classpath. (You can click on Show Command Line to see how the JAR appears there.)
- Click Run. You’ll now see a successful execution.
So what happened? The crux of our example is this:
/*
* ********** AN EXAMPLE OF USING AN INTERFACE ************
* ********** WHERE THE IMPLEMENTATION IS NOT ************
* ********** AVAILABLE UNTIL RUNTIME ************
*
* "Vehicle" is an interface.
* The call to loadClass returns an implementation
* of the interface which it finds knowing
* only the name of the implementing class,
* which is supplied to buildVehicle by the caller.
* We then get the default constructor (because the
* call to getConstructor has no arguments) of the
* implementing class, and call the constructor's
* newInstance method to instantiate the class,
* assigning it to the Vehicle interface.
*/
private void buildVehicle(String className, int target) throws Exception {
Class<?> vehicleClass = Driver.class.getClassLoader().loadClass(className);
Constructor<?> vehicleConstructor = vehicleClass.getConstructor();
Vehicle myVehicle = (Vehicle) vehicleConstructor.newInstance();
myVehicle.accelerateTo(target);
myVehicle.brake();
}
The program compiled cleanly and is able to use methods in the Vehicle interface, even though the implementation of that interface was determined by a value external to the method–in this case, the string passed from the caller. When you added the JAR to the run configuration, you made the implementation available at runtime. The JAR wasn’t required in your build path; all you needed in order to compile is the Vehicle interface.
If someday, we add new vehicles, the program won’t have to change. Instead, we’ll provide a different implementing class name and add the JAR containing the implementation to the runtime classpath. Interfaces save the day!
Interface Methods Are Abstract: Don’t You Believe It!
Remember what we said about the Platypus class having to implement methods to lay eggs and give milk because it can’t inherit them from the interfaces?
Fahgeddaboudit.
Starting with Java 8 (which is probably the version you’re using), this is no longer true. You may define as many instance methods as you like with a body in an interface as “default”, and they will be inherited by implementing classes and don’t need to be overridden.
default void layEggs() {
System.out.println(this.getClass().getName() + " I laid an egg!");
};
Furthermore, you can define any number of static methods with bodies–although, being static, they don’t apply to any given instance of the implementation.
public static void foo() {
System.out.println("foo");
}
Static methods in interfaces are called just as they would be if they appeared in a class instead of an interface. The same is true of default methods.
But what if your class implements two interfaces with the same method signature? Since Java can’t tell which implementation you mean, it’s a compile error, which can be resolved by coding an override of the method in the implementing class.
Interfaces Can’t Be Instantiated: Don’t You Believe It!
There is a special kind of interface, containing one and only one abstract method, called a Functional Interface–and it can be instantiated. There are several that come with Java, one being the Comparator interface used to govern comparison between objects for sorting. Here’s an example:
Comparator<Chicken> = new Comparator<Chicken>() {
@Override
public int compare(Chicken chick1, Chicken chick2) {
return chick1.getBirthOrder() - chick2.getBirthOrder();
}
};
The Delegation Pattern
If default methods and static methods in interfaces doesn’t work for you, you can opt to use the Delegation Pattern: one of a number of Java design patterns which have become popular.
The Delegation pattern involves establishing a class whose methods call methods in other classes to do their work, passing the instance of itself as needed. It’s rather like a central switchboard, where a phone call from the outside gets redirected to one of many recipients on the inside.
Using delegation, we don’t need interfaces at all–although our example does have a use for one. Instead, our classes look like this:
public interface NameProvider {
String getName();
}
public class EggLayer {
public static void layEggs(NameProvider p) {
System.out.println(p.getName() + " has laid an egg!");
};
}
public class MilkGiver {
public static void giveMilk(NameProvider p) {
System.out.println(p.getName() + " has given milk!");
};
}
public class Platypus implements NameProvider {
private String name;
public void giveMilk() {
MilkGiver.giveMilk(this);
}
public void layEggs() {
EggLayer.layEggs(this);
}
@Override
public String getName() {
return name;
}
public Platypus(String name) {
this.name = name;
}
public static void main(String[] args) {
Platypus perry = new Platypus("Perry the Platypus");
perry.giveMilk();
perry.layEggs();
}
}
Notice how the Platypus delegates its work to the EggLayer and MilkGiver classes. At the same time, to allow those classes to work for more than just Platypuses, Platypus implements the NameProvider interface, which EggLayer and MilkGiver can use to get the name of the Platypus–or anything else that implements getName()–being passed to their methods.
And One Last Thing…
What You Need To Know
- Interfaces are promises that an implementing class will perform certain functions.
- Interfaces are also useful for providing constants to the classes that implement them.
- A class may, and often does, implement more than one interface, which can provide a sort of multiple inheritance.
- Interfaces can stand in for implementation classes which are not available at compile time, and which can be loaded dynamically at run time.
- Starting with Java 8, interfaces can contain executable code, within certain limitations.
- Don’t overdo it.
Next: What Makes a Class Go–Methods