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:
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:
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:
{3 4}
12
14
-----
{5}
78.53981633974483
31.41592653589793
{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:
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)
}
{3 4}
12
14
-----
{5}
78.53981633974483
31.41592653589793