Go Interface
What is an interface?
In Go, we use interfaces to declare a set of methods without implementation. The method declaration will have the method name, parameters, and return type.
In large projects, especially when defining a lot of structures, you may notice that some of them have the same methods. For example, a Square
and a Circle
both have an Area()
and a Perimeter()
method. In this case, you can define an interface in order to abstract the common methods.
Other examples include:
Reader
andWriter
implemented by files, network connections, and many other things,Error
,Image
,- etc.
Declaring interfaces
An interface is primarily a type, so the syntax is similar to declaring a structure.
type InterfaceName interface {
MethodName1() ReturnType1
MethodName2() ReturnType2
...
}
More concretely, let's define an interface for a Shape
:
type Shape interface {
Area() float64
Perimeter() float64
}
We could add many other methods to this interface (such as Draw()
, Move()
, etc.), but we will keep it simple for now.
Implementing interfaces
Let's implement this interface for a structure called Square
:
type Square struct {
Side float64
}
func (s Square) Area() float64 {
return s.Side * s.Side
}
func (s Square) Perimeter() float64 {
return 4 * s.Side
}
We can also implement this interface for a Circle
:
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
Note: Be sure to import the math
package.
Using interfaces in functions
Now we can define a function to test if the area of a shape is equal to its perimeter:
func TestShape(s Shape) bool {
return s.Area() == s.Perimeter()
}
and it will work for both Square
and Circle
!
func main() {
s := Square{Side: 4}
fmt.Println(TestShape(s)) // true
c := Circle{Radius: 5}
fmt.Println(TestShape(c)) // false
}
Here we start to see the power of interfaces. We can define a function that works with any type that implements the Shape
interface. This is called polymorphism.
Later on, if we want to add a new type that implements the Shape
interface, we can do it without changing the TestShape()
function.
Empty interfaces
A special case of interfaces is an empty interface that has no methods. It is represented by the interface{}
type. It can be used to represent any type.
One use case of them is as a return type of a parser (that is a function that takes a string and returns the data represented by this string, "42"
will return 42
of type int
for example, but "Hello"
will return "Hello"
of type string
).
func Parse(s string) interface{} {
// ...
}
Multiple interfaces
Last but not least, a structure can implement multiple interfaces.
For example, the File
structure from the os
package implements both the Reader
and the Writer
interface.
As an illustration, let's define a Person
structure that implements two interfaces: Speaker
and Runner
:
type Speaker interface {
Speak()
}
type Runner interface {
Run()
Walk()
}
type Person struct {
Name string
}
func (p Person) Speak() {
fmt.Println("Hello, my name is", p.Name)
}
func (p Person) Run() {
fmt.Println(p.Name, "is running")
}
func (p Person) Walk() {
fmt.Println(p.Name, "is walking")
}