Go language does not have the concept of a class directly. It, however, has a concept of an interface as well as a struct. I’ll illustrate how this can be used to build most of the inheritance constructs that a language like Java or C++ offers.

Inheritance

I’ll use the cliché example of a Rectangle class and a Square class that inherits from it.

Go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main

import "errors"

// Rectangle encapsulates a immutable rectangle
type Rectangle struct {
  width  int32
  height int32
}

// NewRectangle is the constructor of Rectangle
func NewRectangle(width int32, height int32) (*Rectangle, error) {
  if width <= 0 {
    return nil, errors.New("width must be > 0")
  }
  if height <= 0 {
    return nil, errors.New("height must be > 0")
  }
  return &Rectangle{width, height}, nil
}

// Width returns the width
// Guaranteed to be a positive number
func (r Rectangle) Width() int32 {
  return r.width
}

// Height returns the height
// Guaranteed to be a positive number
func (r Rectangle) Height() int32 {
  return r.height
}

func (r Rectangle) Area() int64 {
  return int64(r.width) * int64(r.height)
}

And the cliché Square class that inherits from it

Go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

// Square struct embeds Rectangle struct
// It gets all methods like Width() and Height() of the parent class
// for free
type Square struct {
  Rectangle
}

// NewSquare is the constructor for the Square1 class
func NewSquare(side int32) (*Square, error) {
  rect, err := NewRectangle(side, side)
  if err != nil {
    return nil, err
  }
  return &Square{*rect}, nil
}

Note: This all works fine, if the parent Rectangle class is immutable, however, if it is mutable, then it is better to use composition. However, that’s not a language-specific issue, so, worth discussing in a separate blog post about inheritance vs composition.

Implementation

Go supports interfaces as well. But unlike Java or C++, no explicit declaration is required.

For example, consider a Shape interface

Go
1
2
3
4
5
6
package main

type Shape interface {
 // Area returns the area of the shape
 Area() int64
}

Now, if one adds the following code to the Rectangle struct then both Rectangle and Square will be implementing the shape interface.

Go
1
2
3
4
// Area returns the area of the Rectangle
func (r Rectangle) Area() int64 {
    return int64(r.width) * int64(r.height)
}

The complete example can be seen at https://go.dev/play/p/exbTybm0GGK