W3Basic Logo

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 and Writer 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")
}

© 2023 W3Basic. All rights reserved.

Follow Us: