DEV Community

Cover image for Go - (4) Interfaces
Chathumi Kumarapeli
Chathumi Kumarapeli

Posted on

Go - (4) Interfaces

Introduction

  • Interfaces are a collection of method signatures.
type shape interface {
    area() int
    perimeter() int
}
Enter fullscreen mode Exit fullscreen mode

In the above example, shape is anything that can be used to calculate its area and parameter. If you check the example below, you can see that rectangle implements both the area() and perimeter() methods. So, it means rectangle implements the shape interface.

type rectangle struct {
    length int
    width  int
}

func (r rectangle) area() int {
    return r.length * r.width
}

func (r rectangle) perimeter() int {
    return (r.length + r.width) * 2
}
Enter fullscreen mode Exit fullscreen mode
  • Multiple types can implement the same interface. If there is another struct named circle with area() and perimeter() methods, then it also implements the shape interface.
  • Likewise, a single type can implement multiple interfaces as well. For example, an empty interface like interface {} is always implemented by every type.
  • In Go, interfaces are implemented implicitly. If you check the above function, you can see that we have never explicitly mentioned that the rectangle struct is implemented by shape (like we do in Java).

Named argument interfaces

You can also use named arguments and return data in interface methods. If you check the below function, it is not that clear what are the arguments passing to the Copy() method. You only know that they should be strings.

type Copier interface {
    Copy(string, string) int
}
Enter fullscreen mode Exit fullscreen mode

Therefore, to give more readable interface methods, you can define the interface like below.

type Copier interface {
    Copy(sourceFile string, destFile string) (copiedBytes int)
}
Enter fullscreen mode Exit fullscreen mode

Type assertion

Assume you have several types implemented by the shape interface. But you need to write a function to take the area of a rectangle. In such a case, you should use type assertion.

func getArea(s shape) int {
    r, ok := s.(rectangle)
    if ok {
        return r.length * r.width
    }
    return 0
}
Enter fullscreen mode Exit fullscreen mode

Here, you pass a shape and check whether it's a rectangle. If it is, the value of ok would be true. And r would get a rectangle struct.

Type Switch

Instead of type assertion, you can also use switch cases.

func printNumericValue(num interface{}) {
    switch v := num.(type) {
    case int:
        fmt.Printf("%d", v)
    case string:
        fmt.Printf("%s", v)
    default:
        fmt.Printf("%T", v)
    }
}
Enter fullscreen mode Exit fullscreen mode

Clean Interfaces

  • Keep interfaces as simple as you can.
  • Don't make the interface aware of the underlying types (ex: shape interface should not have methods like isCircle()).

Interfaces are not classes. They are much simpler. They don't have constructors or deconstructors to create/destroy data. There is no hierarchy of passing behavior (parent-to-child behavior).


Top comments (0)