Продвижение методов

Продвижение методов (method promotion) в Go — это механизм, позволяющий вызывать методы вложенной (анонимной) структуры непосредственно через экземпляр внешней структуры. Когда мы вкладываем одну структуру в другую как анонимное поле, все методы вложенной структуры становятся доступны и для внешней структуры.

Это похоже на наследование в ООП, но в Go это реализовано через композицию. Продвижение методов работает только для struct и interface, а также только в том случае, если поле является анонимным (без имени).

Синтаксис

gotype Inner struct {
  Value int
}

func (i Inner) Get() int {
  return i.Value
}

type Outer struct {
  Inner // анонимное поле
}

Теперь методы Inner доступны через Outer.

Пример

Давайте создадим вложенную структуру User с методом GetName и внешнюю структуру Admin, которая будет иметь анонимное поле User:

gopackage main

import "fmt"

type User struct {
  Name string
}

func (u User) GetName() string {
  return u.Name
}

type Admin struct {
  User
  Level int
}

func main() {
  admin := Admin{
    User:  User{Name: "Alice"},
    Level: 5,
  }

  res := admin.GetName()
  fmt.Println(res)
}

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

go"Alice"

Метод GetName был продвинут из структуры User в структуру Admin, и мы можем вызвать его напрямую через экземпляр Admin.

Пример

Если метод вложенной структуры принимает параметры, он также продвигается. Рассмотрим структуру Counter с методом Add:

gopackage main

import "fmt"

type Counter struct {
  Count int
}

func (c *Counter) Add(n int) {
  c.Count += n
}

func (c Counter) Get() int {
  return c.Count
}

type ExtendedCounter struct {
  *Counter
  Name string
}

func main() {
  ec := ExtendedCounter{
    Counter: &Counter{Count: 10},
    Name:  "MyCounter",
  }

  ec.Add(5)
  res := ec.Get()
  fmt.Println(res)
}

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

go15

Методы Add и Get были продвинуты, и мы работаем с ними как с собственными методами структуры ExtendedCounter.

Пример

Если внешняя структура имеет метод с тем же именем, что и у вложенной, то метод внешней структуры переопределяет продвинутый метод. Проверим это на примере:

gopackage main

import "fmt"

type Base struct {
  Value string
}

func (b Base) Get() string {
  return "Base: " + b.Value
}

type Wrapper struct {
  Base
  Value string
}

func (w Wrapper) Get() string {
  return "Wrapper: " + w.Value
}

func main() {
  w := Wrapper{
    Base:  Base{Value: "base"},
    Value: "wrapper",
  }

  res := w.Get()
  fmt.Println(res)
}

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

go"Wrapper: wrapper"

Метод Get структуры Wrapper переопределил продвинутый метод. Чтобы вызвать метод вложенной структуры, нужно обратиться к ней напрямую: w.Base.Get().

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