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 關注
- 96 瀏覽
添加回答
舉報