因為go中嗎,沒有類的概念,也不支援繼承這種操作,但是go具有結構體,並且結構體的組合方式比類更具有擴充套件性以及靈活性。
type identifier struct
例如我們想宣告乙個學生的結構體型別:
type student struct
結構體中字段的型別可以是任何型別,包括函式型別,介面型別,甚至結構體型別本身。例如我們宣告乙個鍊錶中的節點的結構體型別。
type listnode struct
在宣告結構體時我們也可以不給字段指定名字,例如下面這樣
type person struct
我們可以看到其中有乙個int欄位沒有名字,這種我們稱其為匿名字段。
宣告完結構體之後我們需要建立結構體的例項,可以使用如下幾種方法建立,仍然以上面的student結構體為例。
s1 :=
new(student)
//第一種方式
s2 := student
//第二種方式
s3 :=
&student
使用new函式會建立乙個指向結構體型別的指標,建立過程中會自動為結構體分配記憶體,結構體中每個變數被賦予對應的零值。
也可以使用第二種方式生命結構型別,需要注意的是此時給結構體賦值的順序需要與結構體欄位宣告的順序一致。
第三種方式更為常用,我們建立結構體的同時顯示的為結構體中每個字段進行賦值。
宣告完結構體之後可以直接按如下方式操作結構體。
s1.name =
"james"
s1.age =
35
需要注意的是,結構體也仍然遵循可見性規則,要是定義結構體的字段時首字母為小寫在其他包是不能直接訪問該字段的。
如果我們將定義的結構體首字母也變為小寫那麼在其他包內就不能直接建立該結構體,你知道這種情況應該怎麼處理麼?
上面我們提到的匿名字段,可以使用如下方法對其進行操作。
p :=
new(person)
p.id =
"123"
p.int
=10
我們直接通過p.int的方式來訪問結構體中的匿名欄位對其賦值,通過這個例子也可以發現,對於乙個結構體來說,每一種資料型別只能有乙個匿名字段。
在go語言中結構體除了欄位的名稱和型別外還有乙個可選的標籤tag,標記的tag只有reflect包可以訪問到,一般用於orm或者json的資料傳遞,下面這段**演示了如何為結構體打標籤。
type student struct
我們可以使用go自帶的json包將宣告的結構體變數轉變為json字串。
func
tojson
(s *student)
(string
,error
)return
string
(bytes)
,nil
}
如果我們沒有給結構體打標籤輸出的json字串如下所示
如果我們給結構體打過標籤之後輸出的json字串如下所示
之前我們介紹到了匿名字段,結構體作為一種資料型別也可以將其生命為匿名字段,此時我們稱其為內嵌結構體,下面這段**中我們將結構體a嵌入到結構體b中。
type a struct
type b struct
通過內嵌結構體的方式我們可以在結構體b的變數下很方便的操作a中定義的字段。
b :=
new(b)
b.x =
10b.y =
20b.name =
"james"
可以看到在b中我們操作結構體a中定義的字段就像結構體b本身定義的字段一樣自然。
但是如果存在欄位的名稱衝突我們該怎麼辦?例如我們宣告如下乙個結構體c。
type c struct
此時結構體c中也有字段x,但是內嵌的結構體a中也有字段x,如果我們使用如下這種賦值方式會將x的值賦給誰呢?你可以嘗試一下
c :=
new(c)
c.x =
10c.y =
11
如果上面結構體b也有字段x,那麼程式還能成功執行麼?
需要注意的是,內嵌結構體和宣告乙個結構體型別的字段是不同的,例如下面的結構體b的定義方式與上面是完全不同的。
type b struct
你可以嘗試一下在結構體中定義一些複雜型別例如切片,字典等是如何操作的。
方法與函式類似,只不過在方法定義時會在func和方法名之間增加乙個引數,如下所示:
func
(r receiver)
func_name()
其中r被稱為方法的接收者,例如我們下面這個例子:
type person struct
func
(p person)
getname()
string
其中getname方法的接收者為p是person結構體型別,也就是說我們為結構體person繫結了乙個getname方法,我們可以使用如下的方式進行呼叫。
func
main()
fmt.
println
(p.getname()
)}
對於乙個方法來說接收者分為兩種型別:值接收者和指標接收者。上面的getname的接收者就是值接收者。我們再為person結構體定義乙個指標接收者。
func
(p *person)
setname
(name string
)
使用值接收者定義的方法,在呼叫的時使用的其實是值接收者的乙個拷貝,所以對該值的任何操作,都不會影響原來的型別變數。
但是如果使用指標接收者的話,在方法體內的修改就會影響原來的變數,因為指標傳遞的也是位址,但是是指標本身的位址,此時拷貝得到的指標還是指向原值的,所以對指標接收者操作的同時也會影響原來型別變數的值。
而且在go語言中還有一點比較特殊,我們使用值接收者定義的方法使用指標來呼叫也是可以的,反過來也是如此,如下所示:
func
main()
fmt.
println
(p.getname()
) p1 := person
p1.setname
("kobe"
) fmt.
println
(p1.
getname()
)}7.3
介面相當於一種規範,它需要做的是誰想要實現我這個介面要做哪些內容,而不是怎麼做。在go語言中介面的定義如下所示:
type namer inte***ce
在go語言中不需要顯示的去實現介面,只要乙個型別實現了該介面中定義的所有方法就是預設實現了該介面,而且允許多個型別都實現該介面,也允許乙個型別實現多個介面。
type animal inte***ce
type bird struct
func
(b bird)
eat(
)type dog struct
func
(d dog)
eat(
)func
eatwhat
(a animal)
func
main()
d := dog
eatwhat
(b)eatwhat
(d)}
在eatwaht函式中是傳遞乙個animal介面型別,上面的bird和dog結構體都實現了animal介面,所以都可以傳遞到函式中去來實現多型特性。
func
isdog
(a animal)
bool
return
false
}
上面的方法對傳遞進來的引數進行判斷,判斷其是否為dog型別,如果是dog型別的話就會將其進行轉換為v,ok用來表示是否斷言成功。
但是如果我們對於乙個型別有好多種子型別要進行判斷,這樣寫的話顯然是有些複雜,可以使用如下這種方式:
func
whattype
(a animal)
}
空介面是乙個比較特殊的型別,因為其內部沒有定義任何方法所以空介面可以表示任何乙個型別,比如可以進行下面的操作:
var any inte***ce
any =
1fmt.
println
(any)
any =
"hello"
fmt.
println
(any)
any =
false
fmt.
println
(any)
Go 結構體 方法 介面
go 語言中沒有 類 的概念,也不支援像繼承這種物件導向的概念。但是go 語言的結構體與 類 都是復合結構體,而且go 語言中結構體的組合方式比物件導向具有更高的擴充套件性和靈活性。7.1.1 結構體定義 結構體一般定義如下 type identifier struct 例如我們想宣告乙個學生的結構...
Go結構體 方法 介面
go 語言中沒有 類 的概念,也不支援像繼承這種物件導向的概念。但是go 語言的結構體與 類 都是復合結構體,而且go 語言中結構體的組合方式比物件導向具有更高的擴充套件性和靈活性。7.1.1 結構體定義 結構體一般定義如下 type identifier struct 例如我們想宣告乙個學生的結構...
go 修改結構體方法 go結構體方法
golang中的方法是作用在特定型別的變數上,因此自定義型別,都可以有方法,而不僅僅是struct。定義格式 func var struct name funcname var0,var1.return type package main import fmt type test struct na...