Based on https://www.calhoun.io/how-do-interfaces-work-in-go/
Coming from a C background, I did not understand the importance of interfaces at first, but as it turns out, in some way, interfaces can make object oriented programming a lot easier.
What confused me about Go’s interface was that if we still have to write the listed functions again and again for each type we define, what good does it do to have an interface? To understand this, we must first understand inheritance and polymorphism, two very useful concepts from the object oriented programming paradigm.
The basic idea behind inheritance is that a class can inherit properties and methods from its ancestor classes and therefore we don’t need to write the same code twice and inheritance is what we don’t have in Go; and the basic idea behind polymorphism is that a class can implement multiple interfaces such that when we design a function, say, add
, we don’t need to rewrite the same function for each class, like add_int
, add_float
, or add_double
.
Of course, sometimes we can use inheritance to achieve polymorphism. For example, say we have a Tank class and a Bullet class, if we want to define a collision_next_step function for both classes, we can define a shared parent class, say, Movable, for Tank and Bullet, and implement the function directly for Movable. In this case, we are actually using the shared parent class Movable as an interface. Go’s interface is even more straightforward than those from other languages and that’s also why sometimes it can be hard for beginners like me to see what we are doing.
The basic idea behind Go’s interface is that, instead of a specific type, a function can take an interface, and then any object, whatever its type may be, as long as it has implemented the specified interface, can be used with this function. For example, we use the function collision_next_step to detect collision, and we want to use both Bullet and Tank with it, but we don’t want to write
func collision_next_step(t Tank) bool {
...
}func collision_next_step(b Bullet) bool {
...
}
Then we can use an interface, like
func collision_next_step(m Movable) bool {
...
}
But what can be counted as Movable
? Easy, when you declare the Movable interface, you write something like
type Movable interface {
Move() bool
}
That is, any object that has a Move
method, whether it’s a tank or a bullet, can be counted as a Movable
object and can be used with this function. Easy, right? You don’t need to add a single extra character to your definition of Tank or Bullet to achieve polymorphism. (But you do need to ensure that you have implemented a proper move
method for both Tank and Bullet and since we don’t have inheritance we can’t inherit that from a shared parent.)
Looking back at what I have written, I am mildly surprised by the straightforward-ness of the term interface. As we all know, we as developers use all kinds of API, that is, application programming interface, to build our own programs, and when we say a function accepts an interface instead of a type, we are actually saying that we don’t care about the type of the object passed in, as long as it has implemented the APIs that I have specified, which is exactly the point of polymorphism. It totally makes more sense now, right?