Go inheritance

In object-oriented programming (OOP), inheritance is a mechanism that allows a class (subclass) to inherit attributes and methods from another class (parent class). Through inheritance, subclasses can reuse the parent class's code, and can extend or modify their behavior without modifying the parent class.

Go is not a traditional object-oriented programming language; it does not have classes or inheritance.

Go uses structs and interfaces to implement similar functionality.


"Inheritance" in Go

The Go language lacks the class and inheritance concepts found in traditional object-oriented languages; instead, it uses composition and interfaces to achieve similar functionality.

Composition

Composition is the primary way Go implements code reuse. By embedding one struct into another, the child struct can "inherit" the fields and methods of the parent struct.

Example

package main

import "fmt"

// Parent struct
type Animal struct {
    Name string
}

// Parent struct's method
func (a *Animal) Speak() {
    fmt.Println(a.Name, "says hello!")
}

// Child struct
type Dog struct {
    Animal // Embed Animal struct
    Breed  string
}

func main() {
    dog := Dog{
        Animal: Animal{Name: "Buddy"},
        Breed:  "Golden Retriever",
    }

    dog.Speak() // Call the parent struct's method
    fmt.Println("Breed:", dog.Breed)
}

Code Explanation

  • Animalis the parent struct, containing one fieldNameand a methodSpeak。
  • Dogis the child struct, through embeddingAnimalThe struct inheritsAnimalthe fields and methods of.
  • InmainIn the function, we created aDogan instance, and calledSpeakmethod.

2. Interface

Interfaces are the primary way Go implements polymorphism. By defining interfaces, different structs can implement the same methods, thereby achieving polymorphic behavior similar to inheritance.

Example code

Example

package main

import "fmt"

// Define interface
type Speaker interface {
    Speak()
}

// Parent struct
type Animal struct {
    Name string
}

// Implement interface methods
func (a *Animal) Speak() {
    fmt.Println(a.Name, "says hello!")
}

// Child struct
type Dog struct {
    Animal
    Breed string
}

func main() {
    var speaker Speaker

    dog := Dog{
        Animal: Animal{Name: "Buddy"},
        Breed:  "Golden Retriever",
    }

    speaker = &dog
    speaker.Speak() // Call the method through the interface
}

Code Explanation

  • Speakeris an interface, defining aSpeakmethod.
  • AnimalThe struct implementsSpeakerInterface.
  • DogThe struct, through embedding,Animalstruct, indirectly implementingSpeakerInterface.
  • InmainIn the function, we willDogassign the instance toSpeakerinterface, and called through the interfaceSpeakmethod.

Differences between Go and classical inheritance

FeaturesClassical inheritancethe Go way
Code ReuseThrough inheritanceThrough composition (embedding structs)
PolymorphismThrough inheritance and method overridingimplemented through interfaces
Relationship"is-a" relationship"has-a" or "implements" relationship.
FlexibilityThe inheritance relationship is fixedcan be composed at runtime

Complete inheritance simulation:

Example

package main

import "fmt"

// Base class
type Vehicle struct {
    Brand string
}

func (v *Vehicle) Start() {
    fmt.Println(v.Brand, "started")
}

// Derived class
type Car struct {
    Vehicle // Embed Vehicle
    Model  string
}

// Override Start method
func (c *Car) Start() {
    fmt.Println(c.Brand, c.Model, "car started")
}

func main() {
    v := Vehicle{Brand: "Toyota"}
    c := Car{
        Vehicle: Vehicle{Brand: "Honda"},
        Model:  "Civic",
    }
   
    v.Start() // Toyota started
    c.Start() // Honda Civic car started
    c.Vehicle.Start() // Honda started
}

This design in Go avoids many problems of traditional inheritance, such as the fragile base class problem, while providing greater flexibility.

other extensions