Объекты Go обычно строятся вокруг композиции, а не наследования, поскольку используемый вами шаблон очень затруднит для структуры A
возможность делать какие-либо предположения о том, что делает fun2
.Полиморфизм в го делается на уровне интерфейса.Предпочтительный метод заключается в переносе «переопределяемой» функциональности fun2
в отдельный тип интерфейса, который передается в функцию fun1
или сохраняется в объекте, содержащем fun1
.Без специфики того, как вы будете это делать, трудно сделать разумный пример, но это шаблон:
package main
import (
"fmt"
)
type fun2er interface {
fun2()
}
type A struct {
B fun2er
}
func (a* A) fun1() {
fmt.Println("I'm in A.fun1()")
a.B.fun2()
}
type B1 struct {}
func (b B1) fun2() {
fmt.Println("I'm in B1.fun2()")
}
type B2 struct {}
func (b B2) fun2() {
fmt.Println("I'm in B2.fun2()")
}
func main() {
a1 := A{B: B1{}}
a2 := A{B: B2{}}
a1.fun1()
a2.fun1()
}
Это напечатает:
I'm in A.fun1()
I'm in B1.fun2()
I'm in A.fun1()
I'm in B2.fun2()
Редактировать:
Я хотел бы добавить немного больше цвета о том, как это работает под капотом.То, как вы «расширяете» свой тип A
с помощью B
, называется структурным внедрением и в основном является синтаксическим сахаром для добавления поля к B
типа A
, названного так же, как его тип:
type A struct {}
type B struct {
A A
}
Основное отличие состоит в том, что при использовании встраивания структуры вы можете вызывать A
методы непосредственно для B
объекта b.fun1()
.Когда это вызывается, параметр this, подобный a
, который передается в fun1
, - это не весь объект B
, а просто поле A
внутри него (как если бы вы вызывали b.A.fun1()
), котороепочему, когда fun1
вызывает fun2
, он вызывает реализацию A
, поскольку у него нет доступа к объекту B
, в котором он хранится.