In object orientated development, you most commonly work with objects. Objects are like little gadgets that you use to make your program do things. You do so by interacting with methods and properties of the object. That is their interface.
Think of an object like a tiny game console. You read the object’s properties (stare at the screen), and call its methods (push the buttons). (So yes, software development is exactly like playing video games.)
So, let’s say your video game interface has a property (screen), and two methods (left joypad and right button). Suppose you’re designing a robot that will play video games. You could program your robot with code that says “If you’ve got a Super GameGlab, use the screen, the left joypad, and the right button”. Then later maybe add another bit of code that says “If you’ve got a Ultra Game-o-matic, use the same approach, and if you’ve got a Mega GameTark, use the same approach”. The more game consoles you support, the more “use this one too” bits of code you need.
You wind up with a mess like this:
Java
public class SuperGameGlab { public void joypad(int velocity, int direction) { ... } public void button(int state) { ... } public Object screen() { ... } } public class UltraGameOMatic { public void joypad(int velocity, int direction) { ... } public void button(int state) { ... } public Object screen() { ... } } public class MegaGameTark { public void joypad(int velocity, int direction) { ... } public void button(int state) { ... } public Object screen() { ... } } public class Robot { public void PlayGame(Object gameunit) { if (SuperGameGlab.isInstance(gameunit) || UltraGameOMatic.isInstance(gameunit) || MegaGameTark.isInstance(gameunit) { DoSomeRobotGaming(gameunit); } } }
That’s all great. But then you sit down and meditate on this and realize that there are lots of different video game consoles that use this same control interface, and if you want your robot to play all of them, you have to keep expanding your “if” statement and your list of supported classes. But then you realize that you only really need to support the interface itself, not the particular game consoles that implement it.
You might think that’s a good case for a common base class, and you’d be right. You could extract a common base and make your code look more like this:
Java
public class GameConsole { public void joypad(int velocity, int direction) { ... } public void button(int state) { ... } public Object screen() { ... } } public class SuperGameGlab extends GameConsole { } public class UltraGameOMatic extends GameConsole { } public class MegaGameTark extends GameConsole { } public class Robot { public void PlayGame(GameConsole gameunit) { DoSomeRobotGaming(gameunit); } }
That’s certainly a lot cleaner, and if the game consoles don’t actually vary much, you may not even need the descendant classes at all.
But then one day, you decide that your robot’s uncanny accuracy and lightning reflexes could be best employed to plunder a prize from one of those mechanical claw games at the mall. You look at it and realize it’s basically the same concept — a joystick rather than a joypad, and a button. But it’s not a game console, and your generic GameConsole class won’t quite work. But the way you interact with it — the interface — is the same.
So you change up your code like this:
Java
interface IGame { void joypad(int velocity, int direction); void button(int state); Object screen(); } public class GameConsole implements Game { public void joypad(int velocity, int direction) { ... } public void button(int state) { ... } public Object screen() { ... } } public class SuperGameGlab extends GameConsole { } public class UltraGameOMatic extends GameConsole { } public class MegaGameTark extends GameConsole { } public class PlunderCrane implements IGame { public void joypad(int velocity, int direction) { ... } public void button(int state) { ... } public Object screen() { ... } } public class Robot { public void PlayGame(IGame gameunit) { DoSomeRobotGaming(gameunit); } }
The difference is the IGame interface. An interface is a specification for how an object must present itself. It says “anything calling itself an IGame must have these properties and methods.” The actual class can be any kind of class it wants, and doesn’t have to extend anything in particular. This means your PlunderCrane class is free to implement its methods in whatever way it wants, with no dependency on GameConsole, since it isn’t derived from it. But your robot doesn’t need to care, it just knows it presents an IGame interface and that’s how it knows how to play it. This is because the PlayGame method no longer says what kind of class will be passed in, only what kind of interface that class must have.
This flexibility in being able to implement things in different ways is particularly important in things like Collections. For example, a very common thing to do in a collection is to walk the elements of the collection. Nearly every collection is going to implement in iterator of some sort for this purpose. But since collections can vary wildly in their implementation (stacks, queues, arrays, linked lists, etc), requiring them all to extend a common iterator could be problematic. So instead, an iterator interface is used so that any collection can present an iterator as a way to walk the elements of the collection, regardless of how the collection is actually implemented.
Another important difference between interfaces and base classes is that your class can only extend one base class, but you can implement as many interfaces as you like. So your PlunderCrane might implement IGame, and it might also implement IPlushToyDispenser or ICoinOperated, or whatever else your application called for. Or in the case of collections, a collection might implement an iterator interface, a disposable interface, a serializable interface, and so on.
Or to put it another (oversimplified) way, an interface says what an object can do. A base class says how. Generally, you should prefer and program for interfaces rather than classes. It leaves you a lot more flexibility without sacrificing coding clarity.