八,struct
struct的定義如下: type 結構體名 struct{},其中定義的變量不要var,但是仍然是倒序。結構體變量的初始化:結構體名{}括號中的變量按照定義的順序依次填寫;如果不想寫全,或者不想按順序寫,還可以寫成json的格式。(這個爲後續添加變量,而無需修改賦值提供了方便);
匿名變量的訪問:在S1中有一個匿名變量S,對於S中的變量的訪問可以直接寫a.name如13行所示;當然也可以寫成a.S.name(其變量明就是S); 如果S1中又定義了name,寫全可以訪問到S中的變量了;
由於*S和S中的變量的訪問方法相同,所以不能同時存在一個S一個*S的匿名變量;
點擊(此處)摺疊或打開
- package main
- type S struct{
- age int
- name string
- }
- type S1 struct{
- S
- }
- func main()
{
- var a S1=S1{S{10,"tom"}}
- // var a S1=S1{S{name:"tom"}}
- println(a.name);
- }
九 函數&方法
函數的聲明和C也是差不多的。就是變量的類型和返回值都是寫在後面的。還有可以有多個返回值哦。當函數帶上receiver就變成方法了,如第10行所示;
點擊(此處)摺疊或打開
- package main
- type iface interface {
- setName()
- }
- type S1 struct {
- name string
- }
- func (s *S1) setName()
{
- s.name =
"hello"
- }
- func main()
{
- var a S1
- (*S1).setName(&a)
//a.setName()
- println(a.name)
- }
這就a.setName的調用發發有點類似面向對象的寫法了。如果將地10行的*去掉。則調用該函數是會創建一個臨時的S1對象。讓後再對其賦值。等函數退出後,之際調用的便量並沒有得到修改。那麼這個函數還有什麼作用呢? 爲什麼還要提供寫法的?
當然由於slice的特殊性,當slice作爲參數時,其內容被更改了,還是可以反應到實際數組中去的。如下面的代碼,實際打印結果是10;因爲此處傳值指示傳的slice本身,而其指向的數組還是同一個。要想讓b指向另一個數組,就必須得用指針型了。
還有注意點,就是原始類型不能直接作爲receiver;
點擊(此處)摺疊或打開
- package main
- type A []int
- func (s A) setValue()
{
- s[0]
= 10
- }
- /*func
(s *A) setValue()
{
- (*s)[0]
= 10 //注意,此處必須帶*號,編譯器已經無法自動轉換了。如上例的第11行;
- }*/
- func main()
{
- var a = [...]int{1,
2, 3, 4, 5, 6, 7, 8}
- b := A(a[1:6])
- b.setValue()
- println(a[1])
- }
十,interface
上面的例子裏面已經用到了interface;和java相同,interface是一個定義了一系列方法的集合。但是與java不同的是,此處的interface不需要明確標明implents,只要一個類型的被作爲receiver實現了所有的interface的方法,編譯器就能自動識別該類型實現了該interface;如下面的代碼:S1實現了myface;由代碼的19,20行可以發現,無論是送入S1的指針,還是對象,都能實現對setName的調用,如果setName的receiver是*S;則20行會報錯。這就應證了golang手冊中的一句話,T的方法集包含receiver爲T 和*T的所有方法,而*T的方法集只包含receiver爲*T的方法;(更通俗的表達方法時,當參數(receiver是T)時,調用該方法的對象既可以時T,也可以時*T; 當receiver爲*T時,調用時的參數只能時*T; 所以當interface作爲參數時,具體是傳入T還是*T。需要看該對象的具體實現,如果該對象的receiver只有*T,則參數只能傳入*T,否則可以任選T或者*T; 當然只有當receiver爲*T,且參數傳入*T時,纔可能真正修改到T對象的成員變量;
點擊(此處)摺疊或打開
- package main
- type myface interface {
- setName()
- }
- type S1 struct {
- name string
- }
- func (s S1) setName()
{
- s.name =
"hello"
- }
- func test(a myface)
{
- a.setName()
- }
- func main()
{
- var a, b S1
- test(&a)
- test(b)
- println(a.name)
- println(b.name)
- }
當20行的掉用進入到14行時:a的內容爲: {tab = 0xc200035030, data = 0xc20001b020},data是一個緊依賴於上面的一個空間。
由上面兩個聯合得知在19行運行info locals的輸出。
(gdb) i locals
b = {name = {str = 0x0, len = 0}}
&a = 0xc20001b010
以及:p &b.name
$11 = (struct string *) 0x7fffe7f73f58
結合上面的結果可以看出此處輸出的&a不是a的地址,而是某個中間狀態得東西,這個和test(b)進入test函數後的內容類似;
經過查看內存發現 當今如14行時,此處的a的大小是固定的只包含兩個指針的變量,所以sizeof(a)=2*sizeof(void*);而*(a.data)是一個內容空間,而該空間實際是test,傳值時的一個拷貝;
下面在看看*T的receiver的結果:
點擊(此處)摺疊或打開
- package main
- type myface interface {
- setName()
- }
- type S1 struct {
- name string
- age int
- }
- func (s* S1) setName()
{
- s.age=11
- }
- func test(a myface)
{
- a.setName()
- }
- func main()
{
- var a, b S1
= S1{name:"tom"}, S1{name:"java"}
- test(&a)
- test(&b)
- println(a.name)
- println(b.name)
- }
點擊(此處)摺疊或打開
- package main
- type myface interface {//接口,只有一個方法
- setName()
- }
- type S1 struct {
//結構體S1
- name string
- }
- type S2 struct {
//結構體S2
- name string;
- }
- func (this S2)setName(){
//S2的receiver是T;
- this.name="s2";
- }
- func (s *S1) setName()
{ //S1的receiver是*T;
- s.name =
"S1"
- }
- func test1(a myface){
- a.setName();
//調用接口的方法;
- }
- func main(){
- a2:=S2{name:
"aS2"}
- a1:=S1{name:"aS1"};
- //b1:=S1{name:
"bS1"}
- b2:=S2{name:"bS2"}
- test1(&a1)
//S1的receiver是*T,所以值接受*T參數;
- //test1(b1)
- test1(&a2)
//S2的receiver是T,所以接受*T;
- test1(b2)
//S2的receiver是T,所以也接受T;
- println("a1.name=",a1.name);
//S1,這是因a1的內存地址最終能傳到*S1.setName;
- println("a2.name=",a2.name);
//aS2(沒有變),a2的地址雖然能到達test1,但是S2.setName中又複製了一個變量;
- println("b2.name=",b2.name);
//bS2(沒有變),b2到達test1時已經複製了一份,但是到s2的setName又複製了,總計複製兩遍。肯定達不到效果了
- }
詳細參見:http://golang.org/doc/articles/laws_of_reflection.html; 或者其中的blog:http://research.swtch.com/interfaces