Interfaces can be thought of
as a contract on functionality.
Any class that implements an interface must have
all of its methods and properties.
In exchange, the implementing class
can be treated like the interface by other classes, using polymorphism.
It is important to note, that interfaces are not classes,
and cannot have their own instances.
While inheritance is an Is-A relationship,
where one class inherits from another,
interfaces use an Implements relationship,
where one class implements an interface.
Interfaces are usually declared outside of a class.
When declaring interfaces, you would commonly use
one script per interface.
But for the purpose of this example,
we're going to show two in the same script.
By convention, interfaces are declared using a name
that starts with a capital "I", followed by a name
that starts with another capital letter.
Since interfaces normally describe some functionality
that the implementing classes will have,
a lot of interfaces end in the postfix "-able".
It is worth noting however, that this is not mandatory,
and could potentially be misleading,
depending on the interface.
We have declared two interfaces here,
IKillable, and IDamageable.
In IKillable, we have a function called Kill(),
which has a return type of "void", and no parameters.
Any class which implements the IKillable interface
must have a public function which matches this signature.
The IDamageable interface has a generic type T.
This means that anything in the interface can have a generic type.
The function we have in IDamageable, Damage(),
takes a parameter of type T.
When an interface with a generic type is implemented
by a class, the type must be chosen.
Then the corresponding type must be used throughout.
Implementing an interface comes with
some requirements, and some benefits.
In order to implement an interface,
a class must publicly declare all of the methods,
properties, events, and indexers
that exist in that interface.
Failure to do so will result in an error.
The major advantage of interfaces, is that they allow you to define
common functionality across many classes.
Therefore, you are able to safely make assumptions
about what classes can do, based on the interfaces
that they implement.
To implement an interface, simply add a comma
after any inheritance the class has,
followed by the name of the interface.
If the class doesn't inherit, then the comma is not required.
If the interface has a generic type, then the name should be
followed by angle brackets, with the type in them.
In this case, we have class Avatar,
which inherits from, MonoBehaviour, and implements
IKillable and IDamageable, with the type "float".
We must also declare both of the
functions required for these interfaces.
Note that the body of the functions is independent
of the interfaces, and can be implemented however you would like.
These source of interfaces might be useful if there was
a situation in your game where you want to damage or kill everything.
Simply by finding everything that implemented
IKillable or IDamageable, you could be sure
that they will have the Kill() or Damage() functions.
You may be wondering to yourself why you would implement
an interface in a class, when you could justify in a function
once in a class, and have other classes inherit from it.
The easy answer is that you can implement multiple interfaces,
but you cannot inherit from multiple classes.
Therefore, interfaces are a nice way
to provide a wide range of functionality.
The better answer though, is that interfaces
are used to define common functionality
across many classes that are unrelated to each other.
Consider two classes: "Wall" and "Car".
Wall and Car have very little in common with each other,
except that they both are damageable.
Since they are so different from each other,
inheriting from a parent class makes little sense.
Implementing an interface however, works great.