The principle of an interface is simple: it is a contract.
An interface is documented; the methods of an interface do one thing and do it well, and it is up to implementations to ensure that the contract is fulfilled.
For instance, let us take a simple interface, Iterator
. You have Collections.emptyIterator()
which fulfills this interface. For whatever class X, you can declare:
final Iterator<X> iterator = Collections.emptyIterator();
You are guaranteed that this particular implementation will correctly implement the next()
, .hasNext()
and .remove()
methods.
Similarly, if you have a List<X>
and you do:
final Iterator<X> iterator = list.iterator();
you are guaranteed the same.
Accessing through interfaces allow you to tell users that this is the contract they should use for this particular method of yours. The implementation does not matter, and that is the key here.