Встраивание структур

Встраивание структур (struct embedding) в Go — это механизм композиции, позволяющий включить одну структуру в другую как анонимное поле. При встраивании все поля и методы встроенной структуры становятся доступными непосредственно через внешнюю структуру без явного указания имени поля. Это дает возможность переиспользовать функциональность и строить иерархии на основе композиции, а не наследования. Встраивание реализуется через объявление поля с типом структуры, но без имени.

Синтаксис

Встраивание структуры выполняется путем указания типа структуры в качестве поля без имени:

gotype Outer struct {
  Inner // встроенная структура
  Field string
}

В этом примере структура Outer включает в себя структуру Inner через встраивание. Все поля и методы структуры Inner становятся доступными непосредственно через экземпляр Outer.

Пример

Давайте создадим структуру User с базовой информацией о пользователе и встроим ее в структуру Admin:

gopackage main

import "fmt"

type User struct {
  Name string
  Age  int
}

type Admin struct {
  User   // встраивание User
  IsActive bool
}

func main() {
  admin := Admin{
    User: User{
      Name: "Alice",
      Age:  30,
    },
    IsActive: true,
  }

  fmt.Println(admin.Name)    // доступ к полю Name
  fmt.Println(admin.Age)     // доступ к полю Age
  fmt.Println(admin.IsActive)
}

Результат выполнения кода:

go"Alice"
30
true

Как видно из примера, поля Name и Age доступны напрямую через admin, как если бы они были объявлены в структуре Admin.

Пример

Рассмотрим пример с методами встроенной структуры. Создадим структуру Logger с методом Log и встроим ее в структуру Service:

gopackage main

import "fmt"

type Logger struct {
  Prefix string
}

func (l Logger) Log(msg string) {
  fmt.Println(l.Prefix + ": " + msg)
}

type Service struct {
  Logger // встраивание Logger
  Name   string
}

func main() {
  svc := Service{
    Logger: Logger{Prefix: "API"},
    Name:   "UserService",
  }

  svc.Log("Starting service") // вызов встроенного метода
}

Результат выполнения кода:

go"API: Starting service"

Метод Log из встроенной структуры Logger доступен напрямую через Service, что позволяет расширять функциональность без явного делегирования.

Пример

При встраивании структур с одинаковыми именами полей может возникнуть конфликт. В таком случае доступ к полю осуществляется через явное указание встроенной структуры:

gopackage main

import "fmt"

type Address struct {
  City string
}

type Contact struct {
  City string
}

type Person struct {
  Address
  Contact
  Name string
}

func main() {
  p := Person{
    Address: Address{City: "Moscow"},
    Contact: Contact{City: "Berlin"},
    Name:  "Bob",
  }

  fmt.Println(p.Address.City) // явный доступ к полю Address
  fmt.Println(p.Contact.City) // явный доступ к полю Contact
}

Результат выполнения кода:

go"Moscow"
"Berlin"

При наличии конфликта имен компилятор требует явного указания, к какому именно полю осуществляется доступ.

Смотрите также