Interfaces in Java define a contract specification.
Meaning, an arbitrary class X might depend on a feature expected to be implemented by interface Y, but doesn't necessarily care about how that feature is implemented, as long as it produces the result or process that X depends on.
So one could create another class Z to implement interface Y and pass it on to X.
This generates flexibility, as one could always choose to supply class X with a different, possibly more efficient, or specialized version of the feature implemented by interface Y.
Depiction:
class Customer {
String name;
int age;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return this.name; };
public int getAge() { return this.age; }
public String toString(CustomerFormat format) {
return format.formatCustomer(this);
}
}
interface CustomerFormat {
String formatCustomer(Customer customer);
}
class InlineCustomerFormat implements CustomerFormat {
public String formatCustomer(Customer customer) {
return String.format("%s, %d" years old, customer.getName(), customer.getAge());
}
class SimpleCustomerFormat implements CustomerFormat {
public String formatCustomer(Customer customer) {
return String.format("%s-%d", customer.getName(), customer.getAge());
}
}
One important thing to notice about interfaces in Java (and some other languages) is that nothing lets you enforce the actual protocol implementation, other than test code.
What I mean by protocol specification is that, when implementing an interface, you shouldn't only be saying "this will compile", but rather "this will compile and work".
In a nutshell, if you implement an interface, you should make sure that every code that uses that interface, will still produce a valid result if passed an instance of your class. That is part of the contract - which consists of both an API (enforced by the compiler) and the protocol (enforced by the developer's logic, or by tests).