This page looks best with JavaScript enabled

Go interfaces

 ·  🎃 kr0m

In Go, an interface is a set of methods that a structure or type must implement.

Unlike some languages that require a class to explicitly declare that it implements an interface. In Go, the implementation of an interface is implicit, if a type implements the methods required by an interface, it is considered to automatically implement that interface.

Interfaces enable the implementation of polymorphism, meaning that the same interface method can be called using different types, providing different behaviour based on the type.

Additionally, they provide a form of abstraction, hiding the details of implementation and focusing on the essential functionality that an object must provide.


If you are new to the world of Go, I recommend the following previous articles:


Interface example:

vi 01.go
package main

import (
    "fmt"
    "math"
)

type rectangle struct {
    width, height float64
}
type circle struct {
    radius float64
}

func (r rectangle) area() float64 {
    return r.width * r.height
}
func (r rectangle) perimeter() float64 {
    return 2*r.width + 2*r.height
}

func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}
func (c circle) perimeter() float64 {
    return 2 * math.Pi * c.radius
}

type geometry interface {
    area() float64
    perimeter() float64
}

func measure(g geometry) {
    fmt.Println(g)
    fmt.Println(g.area())
    fmt.Println(g.perimeter())
}

func main() {
    r := rectangle{width: 3, height: 4}
    c := circle{radius: 5}

    measure(r)
    fmt.Println("-----")
    measure(c)
}

Same code without interface:

vi 02.go
package main

import (
    "fmt"
    "math"
)

type rectangle struct {
    width, height float64
}
type circle struct {
    radius float64
}

func (r rectangle) area() float64 {
    return r.width * r.height
}
func (r rectangle) perimeter() float64 {
    return 2*r.width + 2*r.height
}

func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}
func (c circle) perimeter() float64 {
    return 2 * math.Pi * c.radius
}

func (r rectangle) measure_r() {
    fmt.Println(r)
    fmt.Println(r.area())
    fmt.Println(r.perimeter())
}

func (c circle) measure_c() {
    fmt.Println(c)
    fmt.Println(c.area())
    fmt.Println(c.perimeter())
}

func main() {
    r := rectangle{width: 3, height: 4}
    c := circle{radius: 5}

    r.measure_r()
    fmt.Println("-----")
    c.measure_c()
}

Code execution:

go run 01.go
{3 4}
12
14
-----
{5}
78.53981633974483
31.41592653589793
go run 02.go
{3 4}
12
14
-----
{5}
78.53981633974483
31.41592653589793

Explanation:

Any object that has the functions area() and perimeter() is considered to implement the geometry interface.

type geometry interface {
    area() float64
    perimeter() float64
}

In this case, the rectangle and circle structures have these functions due to the Go methods .

This makes the code more generic since you only have to call the measure() function without worrying about whether it’s a rectangle r.measure_r() or a circle c.measure_c().

From main, you always call the same function, and you only need to ensure that the required functions in the interface (area/perimeter) are implemented for each shape.

Furthermore, the measure() function can only be used when it is passed an object that satisfies the interface, as it requires it as an input parameter.

func measure(g geometry) {
    fmt.Println(g)
    fmt.Println(g.area())
    fmt.Println(g.perimeter())
}

Interface inclusion from another interface:

Interfaces can include other interfaces; for example, we could have defined an area interface, another perimeter interface, and finally a geometry interface that includes both:

type area interface {
    area() float64
}
type perimeter interface {
    perimeter() float64
}
type geometry interface {
    area
    perimeter
}

The final code would look like this:

vi 03.go

package main

import (
    "fmt"
    "math"
)

type rectangle struct {
    width, height float64
}
type circle struct {
    radius float64
}

func (r rectangle) area() float64 {
    return r.width * r.height
}
func (r rectangle) perimeter() float64 {
    return 2*r.width + 2*r.height
}

func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}
func (c circle) perimeter() float64 {
    return 2 * math.Pi * c.radius
}

type area interface {
    area() float64
}

type perimeter interface {
    perimeter() float64
}

type geometry interface {
    area
    perimeter
}

func measure(g geometry) {
    fmt.Println(g)
    fmt.Println(g.area())
    fmt.Println(g.perimeter())
}

func main() {
    r := rectangle{width: 3, height: 4}
    c := circle{radius: 5}

    measure(r)
    fmt.Println("-----")
    measure(c)
}
go run 03.go
{3 4}
12
14
-----
{5}
78.53981633974483
31.41592653589793
If you liked the article, you can treat me to a RedBull here