Golang Interfaces Explained
Introduction to Go Interfaces
Contracts for Code
In Go, an interface is like a contract. It defines a set of methods that a type must have to be considered a certain kind of thing. It doesn't say how those methods should work, only that they must exist. This is a powerful way to write flexible code that can work with different types in the same way, as long as they all honor the same contract.
interface
noun
A type that specifies a set of method signatures. Any type that implements all methods of an interface is said to satisfy that interface.
Think of it like a USB port. The port is an interface. It doesn't care if you plug in a keyboard, a mouse, or a flash drive. As long as the device has a standard USB plug (it 'satisfies the interface'), the computer knows how to communicate with it. Interfaces in Go work on the same principle: they allow different types to be used interchangeably.
An interface is a collection of method signatures. A type implements an interface by implementing its methods. There is no explicit declaration of intent.
Defining an Interface
The syntax for creating an interface is straightforward. You use the type keyword, followed by the interface name, and then the interface keyword. Inside the curly braces, you list the method signatures required by the contract.
package main
import (
"fmt"
"math"
)
// Shape is an interface for things that have an area.
type Shape interface {
Area() float64
}
Here, we've defined an interface named Shape. Any type that wants to be considered a Shape must have a method called Area that takes no arguments and returns a float64.
Implementing an Interface
In Go, interface implementation is implicit. You don't have to explicitly say that a type implements an interface. If your type has all the methods the interface requires, it automatically satisfies that interface. This is sometimes called "duck typing": if it walks like a duck and quacks like a duck, it must be a duck.
Let's create two different types, a Circle and a Rectangle, and have them both implement the Shape interface.
// Circle struct
type Circle struct {
Radius float64
}
// Rectangle struct
type Rectangle struct {
Width float64
Height float64
}
// Area method for Circle
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
// Area method for Rectangle
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
Notice that both Circle and Rectangle have an Area() method with the correct signature. Because of this, both types now automatically satisfy the Shape interface. We didn't need to write type Circle implements Shape or anything similar.
Now, we can write a function that takes any Shape as an argument. This function doesn't need to know whether it's dealing with a Circle or a Rectangle. It only knows that it can call the Area() method.
// This function works with any type that satisfies the Shape interface.
func PrintArea(s Shape) {
fmt.Printf("The area of the shape is %0.2f\n", s.Area())
}
func main() {
c := Circle{Radius: 5}
r := Rectangle{Width: 4, Height: 6}
PrintArea(c) // Pass a Circle
PrintArea(r) // Pass a Rectangle
}
Program to an interface, not an implementation.
This principle is at the heart of why interfaces are so useful. By depending on the Shape interface instead of the concrete Circle or Rectangle types, our PrintArea function becomes much more flexible and reusable. We could add a Triangle type tomorrow, and as long as it has an Area() method, PrintArea would work with it without any changes.
Now, let's test your understanding of Go interfaces.
What is the primary purpose of an interface in Go?
How does a type in Go officially declare that it implements a specific interface?
Interfaces are a fundamental concept in Go. They provide a way to specify behavior, leading to cleaner, more adaptable, and decoupled code.