I'm going to explain it through an example, which the 'experienced developer' might frown upon, but here we go:
You have to think of an interface as a contract. You promise to implement the methods that are defined, and get all freedom on how to actually implement them. It doesn't matter what performance characteristics you have, all that is important is that it follows the contract.
Let's take java.util.List
and an example. Whatever implementation we use (ArrayList, LinkedList, CopyOnWriteArrayList, ...) we can be sure that they all follow the contract of the rules defined in the List interface. We can more or less use the implementations interchangeably and get the same functionality (and possibly wildly varying performance). We can even define our own list implementation (for example a datastructure that stores its elements on disk, to support very-big-lists). The user (the developer) doesn't really need to know what happens underneath, just that he can change LinkedList
and the application will work
an interface promises it.
a class does it.
You might say that you can provide the same reasoning on (abstract) classes. You have to remember though that a class is not a contract, it is a blueprint. It might follow a contract, but it specifies how
things are done. The ArrayList implementation keeps a private array to hold the elements of a List, it knows how to store and retrieve data, because it doesn't only state what rules should be followed, it knows what to do when, to comply with the requirements.A blueprint might be written in such a way that it fulfills the requirements of multiple contracts.A blueprint cannot be written in a way that it extends on multiple blueprints.
if you replace 'blueprint' with 'class' and replace 'contract' with 'interface', you see what reason lies behind the java type system. You can take a class and extend it with a new blueprint (class), or you can make it implement multiple contracts (interfaces).
This is how java.lang.Iterable
was slapped on most collection classes in Java 1.5, dozens of classes already had superclasses so their blueprints were fixed. By defining the Iterable interface (that promises
to provide an Iterator for that collection), suddenly lots of collection classes were providing an implementation to that promise (adding the .iterator() method), and hence could be passed in the enhanced-for syntax:
for(E item: someInstanceImplementingIterable)
Because Iterable is merly an interface (a contract, not a blueprint) you are free to define your own implementation as long as it follows the rule(s), allowing you to write your own classes of which the instances can be passed into an enhanced-for loop. This could never have been possible with (abstract) classes (in Java) because they force themselves (very high) in the type hierarchy (in this case you can imagine it would be right under Object, and all collection classes would have to extend that new class) and besides, when you would have such functionality in the type hierarchy, every subclass would be forced to provide a valid implementation of the 'iterator()' method, which might not even make sense for certain/most sub-classes (like HashMap) or others in your own game classes. With interfaces you can slap them on arbitrary (low) places in the type hierarchy, to state that specific classes (and potential subtypes) in your game are 'iterable' and others are not.
Pushing contracts/interfaces in your code should be as much 'specialized' as possible, that means it would be low in the type hierachy.
Pushing blueprints/classes in your code should be as much 'generalized' as possible, that means it would be high in the type hierachy.
I hope it's clear, it might be chaotic, but please bear in mind it's 05:24AM here.
I might (have to) revise it later.