1 回答

TA贡献2021条经验 获得超8个赞
我认为通过动态类型,您可能指的是在例如 C++ 和 Java 中,动态绑定本质上是对可以指向派生类的基类的引用(因为派生类“是”基类)。
有人可能会说,基类定义了派生类变形的接口。如果派生类替换了基类的方法,对基类的引用仍然可以访问派生类中的那些方法。
基类可以定义一些方法来为其派生类提供一些“基”功能。如果这些类没有重新定义方法,则可以调用基类的方法。
在 go 中,这两个概念都存在,但它们完全不同。
Go 有一个明确的interface关键字来定义方法签名但没有方法。如果任何值具有具有相同签名的同名方法,则任何值都会隐式满足该接口。
type LivingBeing interface {
TakeInEnergy()
ExpelWaste()
}
接口类型成为代码中的有效类型。我们可以将接口传递给函数,并且在不知道满足该接口的类型的情况下,可以调用它的方法:
func DoLife(being LivingBeing) {
being.TakeInEnergy()
being.ExpelWaste()
}
这是有效的代码,但不是完整的代码。与其他语言的基类不同,接口不能定义函数,只能定义它们的签名。它纯粹是一个接口定义。 我们必须将满足接口的类型与接口本身分开定义。
type Organism struct{}
func (o *Organism) TakeInEnergy() {}
func (o *Organism) ExpelWaste() {}
我们现在有一个organism满足LivingBeing. 它有点像基类,但如果我们想在它的基础上构建,我们不能使用子类化,因为 Go 没有实现它。但是 Go 确实提供了类似的东西,称为嵌入类型。
在这个例子中,我将定义一个新的有机体 Animal,它ExpelWaste()从 中提取LivingBeing,但定义了自己的TakeInEnergy():
type Animal struct {
Organism // the syntax for an embedded type: type but no field name
}
func (a *Animal) TakeInEnergy() {
fmt.Printf("I am an animal")
}
从该代码中不明显的是,因为Animal'sOrganism不是命名字段,所以它的字段和方法可以直接从Animal. 这几乎就像Animal“是一个”有机体。
然而,它*不是*一个有机体。它是一种不同的类型,将对象嵌入视为语法糖以自动将Organism的字段和方法提升为Animal.
由于 go 是静态和显式类型的,DoLife因此不能接受 anOrganism然后传递 an Animal:它没有相同的类型:
/* This does not work. Animal embeds organism, but *is not* an organism */
func DoLife(being *Organism) {
being.TakeInEnergy()
being.ExpelWaste()
}
func main() {
var a = &Animal{Organism{}}
DoLife(&Animal{})
}
cannot use &Animal{} (type *Animal) as type *Organism in argument to DoLife
这就是interface存在的原因——因此Organismand Animal(实际上,甚至Plantor ChemotrophBacteria)都可以作为 LivingBeing.
综上所述,这是我一直在使用的代码:
package main
import "fmt"
type LivingBeing interface {
TakeInEnergy()
ExpelWaste()
}
type Organism struct{}
func (o *Organism) TakeInEnergy() {
}
func (o *Organism) ExpelWaste() {}
type Animal struct {
Organism
}
func DoLife(being LivingBeing) {
being.TakeInEnergy()
being.ExpelWaste()
}
func (a *Animal) TakeInEnergy() {
fmt.Printf("I am an animal")
}
func main() {
var a = &Animal{Organism{}}
DoLife(a)
}
有几个注意事项:
从语法上讲,如果你想声明一个嵌入的文字,你必须显式地提供它的类型。在我的示例
Organism
中,没有任何字段,因此无需声明任何内容,但我仍然将显式类型留在那里以指向正确的方向。DoLife
可以调用TakeInEnergy
LivingBeing 的权利,但Organism
的方法不能。他们只看到嵌入的Organism
.
func (o *Organism) ExpelWaste() {
o.getWaste() //this will always be Organism's getWaste, never Animal's
}
func (o *Organism)getWaste() {}
func (a *Animal)getWaste() {
fmt.Println("Animal waste")
}
同样,如果您只传递嵌入部分,那么它将调用它自己的TakeInEnergy(),而不是Animal; 没有Animal了!
func main() {
var a = &Animal{Organism{}}
DoLife(&a.Organism)
}
总之,
定义显式
interface
并在需要“多态”行为的任何地方使用该类型定义基本类型并将它们嵌入到其他类型中以共享基本功能。
不要期望“基本”类型会“绑定”到“派生”类型的函数。
- 1 回答
- 0 关注
- 114 浏览
添加回答
举报