我正在嘗試學習 Go,但我一直在抨擊它的一些與其他語言相比應用不同的概念。
假設我有一個結構
type Vehicle struct {
Seats int
}
我現在想要另一個結構,它嵌入Vehicle:
type Car struct {
Vehicle
Color string
}
據我了解,該Car結構現在嵌入 Vehicle了.
現在我想要一個可以乘坐任何車輛的功能
func getSeats(v Vehicle){
return v.Seats
}
但是每當我嘗試通過時Car:
getSeats(myCar)
我收到以下錯誤:
cannot use myCar (value of type Car) as Vehicle value in argument to getSeats
但我的 IDE 告訴我myCar有一個Seats屬性!
由此我明白嵌入與繼承不同。
我的問題是;是否有類似 c 的結構繼承的等價物,其中函式可以采用基本結構,或者這是 Go 處理的完全不同的東西?我將如何在“Go way”中實作這樣的東西?
uj5u.com熱心網友回復:
就像你提到的,Go 沒有典型意義上的繼承。嵌入實際上只是語法糖。
嵌入時,您向結構添加一個欄位,其名稱與您要嵌入的型別完全相同。嵌入結構的任何方法都可以在嵌入它們的結構上呼叫,這只不過是轉發它們。
一個標記是,如果嵌入另一個結構的結構已經宣告了一個方法,那么它將優先于轉發它,如果你想這樣想的話,它允許你對函式進行排序。
正如您所注意到的,我們不能Car用作Vehicle即使Car嵌入Vehicle,因為它們嚴格來說不是同一型別。但是任何嵌入的結構都Vehicle將具有由 定義的所有方法Vehicle,因此如果我們定義一個Vehicle實作的介面,則所有嵌入的型別也Vehicle應該實作該介面。
例如:
package main
import (
"fmt"
)
type Seater interface {
Seats() int
}
type Vehicle struct {
seats int
}
func (v *Vehicle) Seats() int {
return v.seats
}
type Car struct {
Vehicle
Color string
}
type Bike struct {
Vehicle
Flag bool
}
// A bike always has 1 seat
func (b *Bike) Seats() int {
return 1
}
type Motorcycle struct {
Vehicle
Sidecar bool
}
// A motorcycle has the base amounts of seats, 1 if it has a side car
func (m *Motorcycle) Seats() int {
return m.Vehicle.seats 1
}
func getSeats(v Seater) int {
return v.Seats()
}
func main() {
fmt.Println(getSeats(&Bike{
Vehicle: Vehicle{
seats: 2, // Set to 2 in the Vehicle
},
Flag: true,
}))
fmt.Println(getSeats(&Motorcycle{
Vehicle: Vehicle{
seats: 1,
},
Sidecar: true,
}))
fmt.Println(getSeats(&Car{
Vehicle: Vehicle{
seats: 4,
},
Color: "blue",
}))
}
這列印:
1
2
4
在呼叫方法的情況下,Bike這Bike.Seats就是為什么1即使它的seats值是它也會回傳的Vehicle原因2。
在方法也被呼叫的情況下,但是在這里我們可以訪問嵌入型別并仍然使用它來獲取結果Motorcycle。Motorcycle.Seats
在 的情況下Car,該Vehicle.Seats方法被呼叫,因為Car不會“覆寫” Seats。
uj5u.com熱心網友回復:
這是我開始使用 Go 時關心的事情。我認為使用 Java 等 OOP 語言的概念是自然的做事方式。好吧,它不是,Go 不是面向物件的,它沒有通過繼承實作多型性。最后,Go 支持的組合從繼承提供的東西中獲取了好的東西,并洗掉了增加更多開銷而不是有效幫助的東西。Go 對多型性的回答是介面。Go 力求簡單,你通常只能通過一種明顯的方式實作目標。假設您有一個像這樣的 Java 類:
class Base {
private int attribute;
public int getAttribute() {
return this.attribute;
}
}
class Something extends Base {}
在java中,每個物件都在堆上,并且你有指向它的指標。它可以是null,這也意味著當你傳遞它時它具有相同的大小。這就是為什么你可以更自由地傳遞物件的原因之一(無論你傳遞Base還是Something,只要引數型別是超類,它就會編譯)。Go 旨在更有效地管理記憶體并使用堆疊而不是堆,甚至堆的碎片更少。當您宣告 astruct時,該結構的大小由它保存的資料決定,因此您不能將它傳遞到嵌入結構所屬的位置。我們舉個例子:
type Base struct {
attribute int32
}
func (b *Base) Attribute() int32 {
return b.attribute
}
type Something struct {
garbage int32
Base
}
WhileBase有 4 個位元組,Something有 8 個位元組,并且attribute有不同的偏移量。如果編譯器允許您Something代替傳遞Base,您將訪問garbage而不是attribute. 如果你想獲得這種行為,你必須使用介面。幸運的是,您只需宣告以下內容:
type BaseInterface interface {
Attribute() int32
}
現在你有了這個介面,你可以在所有嵌入 Base 的結構上創建通用函式(除非它還嵌入了Attribute在同一級別上具有方法名稱的其他東西),如下所示:
func DoubleOfBaseAttribute(base BaseInterface) int32 {
return base.Attribute() * 2
}
這也稱為動態調度,它隱含地使用了 C 或 Java 等語言。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/414945.html
標籤:
