Go does not support traditional inheritance mechanisms from object-oriented programming; instead, it employs composition for code reuse. Structs in Go can reuse fields and methods by embedding other structs, a technique analogous to inheritance but more flexible and lightweight.
For example, consider a basic Car struct representing common attributes and functionalities for all cars. We can then create a more specific ElectricCar struct by embedding the Car struct:
gotype Car struct { Make string Model string Year int } func (c *Car) Start() { fmt.Println("Car is starting") } type ElectricCar struct { Car // embedding the Car struct BatteryCapacity int } func (e *ElectricCar) Charge() { fmt.Println("Charging the electric car") }
In this example, ElectricCar automatically gains the Make, Model, Year fields and the Start method through embedding the Car struct, while also adding its own specific fields and methods, such as the BatteryCapacity field and the Charge method.
Regarding generics, Go did not natively support generics in its earlier versions. However, starting from Go 1.18, Go introduced support for generics. This enables developers to write more flexible and reusable code. Generics allow functions, types, etc., to be defined with type parameters, enabling code sharing across multiple data types without sacrificing type safety.
Here is a simple example using Go generics, defining a generic function PrintSlice that can accept slices of any type:
gofunc PrintSlice[T any](s []T) { for _, v := range s { fmt.Println(v) } } func main() { PrintSlice[int]([]int{1, 2, 3}) PrintSlice[string]([]string{"hello", "world"}) }
In this example, the PrintSlice function uses the type parameter T to handle slices of different types. any is a type constraint indicating that T can be any type. This allows a single function to process multiple data types without duplicating code for each type.