It starts with a dog. In particular, a pug.

The pug has various behaviors:
public class Pug
{
private String name;
public Pug(String n)
{
name = n;
}
public String getName()
{
return name;
}
public String bark()
{
return "Arf!";
}
public boolean hasCurlyTail()
{
return true;
}
}

And you have a Labrador, who also has a set of behaviors.
public class Lab
{
private String name;
public Lab(String n)
{
name = n;
}
public String getName()
{
return name;
}
public String bark()
{
return "Woof!";
}
public boolean hasCurlyTail()
{
return false;
}
}
We can make some pugs and labs:
Pug pug = new Pug("Spot");
Lab lab = new Lab("Fido");
And we can invoke their behaviors:
pug.bark() -> "Arf!"
lab.bark() -> "Woof!"
pug.hasCurlyTail() -> true
lab.hasCurlyTail() -> false
pug.getName() -> "Spot"
lab.getName() -> "Fido"

Let's say I run a dog kennel and I need to keep track of all the dogs I'm housing. I need to store my pugs and labradors in separate arrays:
public class Kennel
{
Pug[] pugs = new Pug[10];
Lab[] labs = new Lab[10];
public void addPug(Pug p)
{
...
}
public void addLab(Lab l)
{
...
}
public void printDogs()
{
// Display names of all the dogs
}
}
But this is clearly not optimal. If I want to house some poodles, too, I have to change my Kennel
definition to add an array of Poodles
. In fact, I need a separate array for each kind of dog.
Insight: both pugs and labradors (and poodles) are types of dogs and they have the same set of behaviors. That is, we can say (for the purposes of this example) that all dogs can bark, have a name, and may or may not have a curly tail. We can use an interface to define what all dogs can do, but leave it up to the specific types of dogs to implement those particular behaviors. The interface says "here are the things that all dogs can do" but doesn't say how each behavior is done.
public interface Dog
{
public String bark();
public String getName();
public boolean hasCurlyTail();
}
Then I slightly alter the Pug
and Lab
classes to implement the Dog
behaviors. We can say that a Pug
is a Dog
and a Lab
is a Dog
.
public class Pug implements Dog
{
// the rest is the same as before
}
public class Lab implements Dog
{
// the rest is the same as before
}
I can still instantiate Pug
s and Lab
s as I previously did, but now I also get a new way to do it:
Dog d1 = new Pug("Spot");
Dog d2 = new Lab("Fido");
This says that d1
is not only a Dog
, it's specifically a Pug
. And d2
is also a Dog
, specifically a Lab
.
We can invoke the behaviors and they work as before:
d1.bark() -> "Arf!"
d2.bark() -> "Woof!"
d1.hasCurlyTail() -> true
d2.hasCurlyTail() -> false
d1.getName() -> "Spot"
d2.getName() -> "Fido"
Here's where all the extra work pays off. The Kennel
class becomes much simpler. I need only one array and one addDog
method. Both will work with any object that is a dog; that is, objects that implement the Dog
interface.
public class Kennel
{
Dog[] dogs = new Dog[20];
public void addDog(Dog d)
{
...
}
public void printDogs()
{
// Display names of all the dogs
}
}
Here's how to use it:
Kennel k = new Kennel();
Dog d1 = new Pug("Spot");
Dog d2 = new Lab("Fido");
k.addDog(d1);
k.addDog(d2);
k.printDogs();
The last statement would display:
Spot
Fido
An interface give you the ability to specify a set of behaviors that all classes that implement the interface will share in common. Consequently, we can define variables and collections (such as arrays) that don't have to know in advance what kind of specific object they will hold, only that they'll hold objects that implement the interface.
void jump(int)
method, then you are an IPerson). Haskell is very strongly typed, but also uses type inference and some very nifty stuff with TypeClasses to handle similar situations.