Method set и встраивание
Для потокового чтения Миша завёл новый тип FileStream и вложил в него Reader. Методы чтения появились у FileStream сами, без единой строчки кода. Аня на ревью напомнила: это композиция, а не наследование.
набор методов указателя шире, чем у значения — от него зависит, какие интерфейсы тип удовлетворяет; а встраивание — это композиция, при которой методы и поля продвигаются наружу.
Method set
Набор методов типа определяет, какие интерфейсы он удовлетворяет. Формулируется просто:
- методы типа
T— все, у которых получательT; - методы типа
*T— методы с получателемTи методы с получателем*T.
type Greeter interface{ Greet() string }
type English struct{}
func (English) Greet() string { return "Hello" } // value-метод
var g Greeter = English{} // ок
var g2 Greeter = &English{} // тоже ок
Пока метод value-receiver, разницы нет. Стоит сделать его pointer-receiver — и English{} интерфейс уже не удовлетворит.
Встраивание
Поле без имени встраивает другой тип, и его методы и поля «продвигаются» наружу. Так в FileStream встроен Reader — весь набор методов чтения доступен бесплатно:
type FileStream struct {
io.Reader // встроенный Reader — методы продвигаются наружу
offset int64
}
f, _ := os.Open("report.pdf")
fs := FileStream{Reader: f, offset: 0}
data, err := fs.Read(buf) // метод Read продвинут от встроенного io.Reader
Продвигаются и поля встроенной структуры:
type Meta struct {
Name string
}
func (m Meta) DisplayName() string { return m.Name }
type FileStream struct {
Meta
Offset int64
}
fs := FileStream{Meta: Meta{Name: "report.pdf"}, Offset: 0}
fmt.Println(fs.Name) // report.pdf — поле продвинуто
fmt.Println(fs.DisplayName()) // report.pdf — метод продвинут
Это не наследование
FileStream сам удовлетворяет всему, что умеет Meta. Но встраивание — композиция, а не наследование: Meta остаётся отдельным полем, FileStream не становится подтипом Meta, и методы Meta получают копию Meta, а не FileStream целиком. Попытка сделать из встраивания классическое наследование обычно заканчивается путаницей.
Вы узнаете:
- как интерфейсы собираются из method set'ов
- как решаются конфликты имён при встраивании двух типов
Проверьте себя
User встраивает Named, но имеет и собственное поле Name. Что вернёт u.DisplayName() и u.Name?
Встраивание по указателю *Named — какие методы продвинуты в method set значения User?
Чем method set значения отличается от method set указателя?
правильных ответов
Method set: у указателя — оба набора методов, у значения — только value-методы. Встраивание продвигает поля и методы, но это композиция.
10 000 ₽ · 60 минут · онлайн