Ответ
В Go нет классического наследования, как в языках вроде Java или C++. Вместо этого Go использует композицию через встраивание (embedding).
Встраивание для переиспользования кода
Вы можете встроить одну структуру в другую, и поля и методы встроенной структуры становятся доступными напрямую у внешней структуры. Это обеспечивает переиспользование кода, но не является наследованием.
Пример:
// "Базовый" тип
type Animal struct {
Name string
}
func (a *Animal) Speak() {
fmt.Printf("%s издает звукn", a.Name)
}
// "Производный" тип
type Dog struct {
Animal // Встраивание Animal в Dog
Breed string
}
// У Dog есть свой собственный метод
func (d *Dog) Bark() {
fmt.Printf("%s лает!n", d.Name)
}
func main() {
d := Dog{
Animal: Animal{Name: "Рекс"},
Breed: "Овчарка",
}
d.Speak() // Вызов метода из встроенной структуры Animal
d.Bark() // Вызов собственного метода Dog
}
Важное замечание: Если у структуры Dog
будет свой метод Speak()
, он не переопределит, а затенит метод из Animal
. Метод Animal
все еще можно будет вызвать явно: d.Animal.Speak()
.
Интерфейсы для полиморфизма
Полиморфизм в Go достигается с помощью интерфейсов. Интерфейс определяет набор методов. Любой тип, который реализует все методы интерфейса, неявно удовлетворяет этому интерфейсу.
// Интерфейс, описывающий поведение
type Speaker interface {
Speak()
}
// Функция, работающая с любым типом, который удовлетворяет интерфейсу Speaker
func MakeSound(s Speaker) {
s.Speak()
}
func main() {
dog := &Dog{Animal: Animal{Name: "Рекс"}}
// cat := &Cat{...} // Предположим, у Cat тоже есть метод Speak()
MakeSound(dog) // Работает, т.к. *Dog реализует Speak()
// MakeSound(cat) // Тоже будет работать
}
Итог: Go сознательно отказывается от наследования в пользу более гибкого и ясного подхода: композиция > наследование.