Update: the interface keyword has since been removed from Dart.
Interfaces are useful because they allow you to switch implementations of a class, whilst still allowing validation that the type being passed in meets the requirements of the interface.
Take the following (often used) example:
interface Quackable {
void quack();
}
This defines the requirements of a class that will be passed to a method such as:
sayQuack(Quackable quackable) {
quackable.quack();
}
which allows you to make use of any implementation of a Quackable object, such as:
class MockDuck implements Quackable {
void quack() => print("quack");
}
class EnterpriseDuck implements Quackable {
void quack() {
// connect to three enterprise "ponds"
// and eat some server bread
// and say "quack" using an messaging system
}
}
Both of these implementations will work with the sayQuack() function, but one requires significantly less infrastructure than the other.
sayQuack(new EnterpriseDuck());
sayQuack(new MockDuck());
I use this pattern all the time in the Java world, when building solutions that make use of some "enterprise duck". When developing locally, all I simply need is to be able to call the sayQuack() function and return some hard-coded, mock data.
Duck typing
Because Dart is optionally typed, you don't actually need to use the interface, simply writing a class that contains the correct method signature will work (although the tools won't be able to validate it).
class Person { // note: no implements keyword
void quack() => "I'm not a duck";
}
sayQuack(new Person()); // provides the quack method, so this will still work
All classes are interfaces
Finally, all Classes are also interfaces. This means that even though a third party system may have been written without using interfaces, you can still use a concrete class as though it were an interface.
For example, imagine the following enterprise library:
class EnterpriseDuck { // note: no implements keyword
void quack() {
// snip
}
}
sayQuack(EnterpriseDuck duck) { // takes an instance of the EnterpriseDuck class
duck.quack();
}
And you want to pass a mock duck into the sayQuack method in a way that the type checker can validate. You can create your mockDuck to implement the interface implied by EnterpriseDuck, simply by using the EnterpriseDuck as an interface:
class MockDuck implements EnterpriseDuck {
void quack() => "I'm a mock enterprise duck";
}
Multiple Inheritance
In terms of multiple inheritance, this is not possible in Dart. You can, however, implement multiple interfaces and provide your own implementations of the required methods, eg:
class MultiDuck implements Quackable, EnterpriseDuck, Swimable {
// snip...
}
Interfaces can have default classes
As you use Dart, you will find that most "classes" are actually interfaces. List, String etc... are all interfaces with default implementations provided. When you call
List myList = new List();
you are actually using a List interface, and the new keyword redirects from the interface to an underlying default List implementation.
With regards to developing in a team
Interfaces are useful in team development, even in the open source world. The interface defines the methods and properties that you should be building so that your component works with my component. You can build your own test implementation of that interface, and I can build my concrete implementation of that interface, and when we're both done, we can integrate. Without the published, shared interface, I would need to provide my concrete implementation before you could really get started.
Hope that helps!