六、golang中的結構體和方法、接口


結構體:

1、用來自定義復雜數據結構

2、struct里面可以包含多個字段(屬性)

3、struct類型可以定義方法,注意和函數的區分

4、strucr類型是值類型

5、struct類型可以嵌套

6、go語言中沒有class類型,只有struct類型

struct聲明:

         type  標識符 struct{

                   field1 type

                   field2 type

}

例子:

         type Student struct{

         Name string

         Age  int

         Score int

}

struct中字段訪問,和其他語言一樣,使用點

         例子:

                   var stu Student            //拿結構題定義一個變量

                   stu.Name=”tony”

                   stu.Age=18

                   stu.Score=20

                   fmt.Printf(“name=%s,age=%d,score=%d”,stu.Name,stu.Age,stu.Sore)

 

struct定義的三種形式    初始化的三種方式

         a、var stu Student

         b、var stu *Student=new(Student)

         c、var stu *Student=&Student{}

其中b和c返回的都是指向結構體的指針,訪問形式如下:

         a、stu.Name、stu.Age  和stu.Score 或者(*stu).Name、 (*stu).Age等

如果是指針形式可以用上面的普通的方式訪問,其實就自動轉化為指針訪問的形式

package main

import (
   "fmt"
)

type Student struct{
   Name string
   Age int
   score float32
}

func main(){
   //聲明方式一
   var stu Student

   stu.Name="hua"
   stu.Age=18
   stu.score=80
   
   //聲明方式二
   var stu1 *Student =&Student{
      Age:20,
      Name:"hua",
   }
   
   //聲明方式三
   var stu3 =Student{
      Age:20,
      Name:"hua",
   }

   fmt.Printf(stu1.Name)
   fmt.Printf(stu3.Name)
}

 

struct內存布局

例子:

 

package main

import(
   "fmt"
)

type Student struct{
   Name string
   Age int
   score float32
}

func main(){
   var stu Student
   stu.Name="hua"
   stu.Age=18
   stu.score=80

   fmt.Print(stu)
   fmt.Printf("Name:%p\n",&stu.Name)
   fmt.Printf("Age:%p\n",&stu.Age)
   fmt.Printf("score:%p\n",&stu.score)
}
{hua 18 80}Name:0xc04204a3a0
Age:0xc04204a3b0
score:0xc04204a3b8
這里int32是4字節,64是8字節

鏈表的定義:

type Student struct{

         name string

         next* Student

}

每個節點包含下一個節點的地址,這樣把所有的節點串起來,通常把鏈表中的每一個節點叫做鏈表頭

 

遍歷到最后一個元素的時候有個特點,就是next這個指針指向的是nil,可以從這個特點來判斷是否是鏈表結束

單鏈表的特點:只有一個字段指向后面的結構體

         單鏈表只能從前往后遍歷

雙鏈表的特點:有兩個字段,分別指向前面和后面的結構體

         雙鏈表可以雙向遍歷

 

鏈表操作:

1、生成鏈表及遍歷鏈表操作

 

package main

import (
   "fmt"
)

type Student struct{
   Name string
   Age int
   Score float32
   next *Student
}

func main(){
   var head Student
   head.Name="hua"
   head.Age=18
   head.Score=80

   var stu1 Student
   stu1.Name="stu1"
   stu1.Age=20
   stu1.Score=100

   head.next=&stu1

   //遍歷
   var p *Student=&head   //生成p指針,指向head
   for p!=nil{           //這里p就是head結構體,所以要從第一個遍歷
      fmt.Println(*p)
      p=p.next
   }
}
D:\project>go build go_dev / example/example3

D:\project>example3.exe
{hua 18 80 0xc042078060}  這里第三個值指向的是下一個結構體
{stu1 20 100 <nil>}

上面的程序不規范,修改如下:
package main

import (
   "fmt"
)

type Student struct{
   Name string
   Age int
   Score float32
   next *Student
}

func trans(p *Student){
   for p!=nil { //這里p就是head結構體,所以要從第一個遍歷
      fmt.Println(*p)
      p = p.next
   }
}

func main(){
   var head Student
   head.Name="hua"
   head.Age=18
   head.Score=80

   var stu1 Student
   stu1.Name="stu1"
   stu1.Age=20
   stu1.Score=100   //這里默認第二個鏈表為nil

   head.next=&stu1

   //var p *Student=&head
   trans(&head) //生成p指針,指向head
}

插入鏈表的方法:

 

1、尾部插入法,就在鏈表的尾部插入結構體
代碼如下:
package main

import(
   "fmt"
)

type Student struct{
   Name string
   Age int
   Score float32
   next *Student
}

func trans(p *Student){
   for p!=nil { //這里p就是head結構體,所以要從第一個遍歷
      fmt.Println(*p)
      p = p.next
   }
}

func main() {
   var head Student
   head.Name = "hua"
   head.Age = 18
   head.Score = 80

   var stu1 Student
   stu1.Name = "stu1"
   stu1.Age = 20
   stu1.Score = 100

   var stu2 Student
   stu2.Name="stu2"
   stu2.Age=22
   stu2.Score=90

   head.next=&stu1
   stu1.next=&stu2
   trans(&head)

   }
2、尾部循環插入
package main

import (
   "fmt"
   "math/rand"
)

type Student struct{
   Name string
   Age int
   Score float32
   next *Student
}

func trans(p *Student){
   for p!=nil { //這里p就是head結構體,所以要從第一個遍歷
      fmt.Println(*p)
      p = p.next
   }
}

//尾部循環插入數據
func trans2(tail *Student){
   for i:=0;i<10;i++{
      stu:=&Student{
         Name:fmt.Sprintf("stu%d",i),
         Age:rand.Intn(100),
         Score:rand.Float32()*100,
      }
      //注意下面的是指針
      tail.next=stu   //鏈表是指向下一個
      tail=stu        //更新最后一個鏈表
   }
}

func main(){
   var head Student
   head.Name="hua"
   head.Age=18
   head.Score=100
   
   //下面這兩個都是根據head這個鏈表結構體產生
   trans2(&head)
   trans(&head)
}
3、頭部插入
1、注意給指針分配內存空間
2、用指針的方式才可以分配內存,如果用變量就不行(牽扯到地址就用指針)
package main

import(
   "fmt"
   "math/rand"
)

type Student struct{
   Name string
   Age int
   Score float32
   next *Student
}

func trans(p *Student){
   for p!=nil{
      fmt.Println(*p)
      p=p.next
   }
}

func main(){
   //因為這是指針,所以要給指針分配空間,下面是給指針分配內存空間的兩種方法
   //var head *Student=&Student{}
   var head *Student=new(Student)
   head.Name="hua"
   head.Age=18
   head.Score=100

   //從頭插入
   for i:=0;i<10;i++{
      stu:=Student{
         Name:fmt.Sprintf("stu%d",i),
         Age:rand.Intn(100),
         Score:rand.Float32()*100,
      }
      //頭部插入必須要傳遞指針才可以,首先頭部插入一個,鏈表指向第一個,但是插入的鏈表地址被覆蓋
      stu.next=head
      head=&stu    //指針賦值

   }
   trans(head)
}
頭部插入和尾部插入的區別是
頭部插入:需要用指針的方式來插入
尾部插入:直接插入就可以了(變量形式插入)
優化代碼
package main

import(
   "fmt"
   "math/rand"
)

type Student struct{
   Name string
   Age int
   Score float32
   next *Student
}

//打印函數
func trans(p *Student){
   for p!=nil{
      fmt.Println(*p)
      p=p.next
   }
}

//這里的head是指針變量副本,這里要接受指針的指針,head1 * Student是指針變量的副本
func insertHead(head1 **Student){
   //從頭插入
   for i:=0;i<10;i++ {
      stu := Student{
         Name:  fmt.Sprintf("stu%d", i),
         Age:   rand.Intn(100),
         Score: rand.Float32() * 100,
      }
      //因為參數是指針的指針,所以這里要傳遞指針的指針,
      stu.next = *head1
      *head1 = &stu
   }
}

func main(){
   var head *Student=new(Student)
   head.Name="hua"
   head.Age=18
   head.Score=100

   //因為這個函數要改變指針變量的值,所以要傳遞指針的地址進去
   insertHead(&head)
   trans(head)
}
理解:
如下:這里的insertHead中的head1是head的副本,開始head1和head是指向同一個內存地址,當head1=&stu的時候head1的地址,也就是head的副本的地址就變化了,但是head還是沒有變化的。所以要改變指針的地址,也就是head的地址,這里函數必須要傳遞指針的指針才可以,在指針的基礎之上多加一個*,
func insertHead(head1 **Student){}
然后傳遞的時候要傳遞指針的地址,如 insertHead(* head)
小結: 要改變指針變量的值,就要傳遞指針的指針進去
指針還有二級指針,三級指針等

刪除鏈表

刪除指定節點:

思路:

1、遍歷,

2、遍歷當前節點的上個節點的next等於當前節點的下一個節點,這個節點就刪除了

3、如果第一次沒有找到,那么就往后移動位置,即當前節點的上級節點等於當前節點,當前節點的下一個節點賦值給當前節點,

4、下面這個代碼是有問題的,主要是這是一個副本。頭部插入會有問題

代碼:

 

package main

import (
   "fmt"
   "math/rand"
)

type Student struct{
   Name string
   Age int
   Score float32
   next *Student   //指向下一個節點
}

func trans(p *Student)  {
   for p!=nil{
      fmt.Println(*p)
      p=p.next
   }
}

//頭部插入
func insertHead(head **Student){
   //從頭插入
   for i:=0;i<10;i++ {
      stu := Student{
         Name:  fmt.Sprintf("stu%d", i),
         Age:   rand.Intn(100),
         Score: rand.Float32() * 100,
      }
      //因為參數是指針的指針,所以這里要傳遞指針的指針,
      stu.next = *head
      *head = &stu
   }
}

func delNode(p * Student){
   //臨時變量保存上一個節點
   var prev *Student=p

   /*
   遍歷鏈表
   1、首先判斷當前鏈表節點是否等於要刪除的鏈表,如果是那么把當前鏈表節點上一個節點等於
   當前鏈表節點的下一個節點
   2、如果沒有找到,那么當前鏈表節點就等於上個節點,當前鏈表節點就指向下個節點,也就是往后移動位置
   */
   for p!=nil{
      if p.Name=="stu6"{
         prev.next=p.next
         break
      }
      //如果沒有找到,那么p就等於上個節點,p就指向下個節點
      prev=p
      p=p.next
   }
}

func main(){
   var head *Student=new(Student)
   head.Name="hua"
   head.Age=18
   head.Score=100

   insertHead(&head)
   delNode(head)
   trans(head)

}

怎么在上面stu6后面插入一個節點?

思路:

1、首先生成一個節點,讓這個節點的下一個節點等於stu6的下一個節點

2、再讓stu6的下一個節點指向插入的這個節點

 

package main

import (
   "fmt"
   "math/rand"
)

type Student struct{
   Name string
   Age int
   Score float32
   next *Student   //指向下一個節點
}

func trans(p *Student)  {
   for p!=nil{
      fmt.Println(*p)
      p=p.next
   }
}

//頭部插入
func insertHead(head **Student){
   //從頭插入
   for i:=0;i<10;i++ {
      stu := Student{
         Name:  fmt.Sprintf("stu%d", i),
         Age:   rand.Intn(100),
         Score: rand.Float32() * 100,
      }
      //因為參數是指針的指針,所以這里要傳遞指針的指針,
      stu.next = *head
      *head = &stu
   }
}

func delNode(p * Student){
   //臨時變量保存上一個節點
   var prev *Student=p

   /*
   遍歷鏈表
   1、首先判斷當前鏈表節點是否等於要刪除的鏈表,如果是那么把當前鏈表節點上一個節點等於
   當前鏈表節點的下一個節點
   2、如果沒有找到,那么當前鏈表節點就等於上個節點,當前鏈表節點就指向下個節點,也就是往后移動位置
   */
   for p!=nil{
      if p.Name=="stu6"{
         prev.next=p.next
         break
      }
      //如果沒有找到,那么p就等於上個節點,p就指向下個節點
      prev=p
      p=p.next
   }
}

//在stu5后面插入一個鏈表
func addNode(p *Student,newNode * Student){
   for p!=nil{
      if p.Name=="stu5"{
         newNode.next=p.next
         p.next=newNode
         break
      }
      p=p.next
   }
}

func main(){
   var head *Student=new(Student)
   head.Name="hua"
   head.Age=18
   head.Score=100

   insertHead(&head)
   delNode(head)
   trans(head)

   var newNode *Student=new(Student)
   newNode.Name="stu1000"
   newNode.Age=18
   newNode.Score=100
   addNode(head,newNode)
   trans(head)

}

雙向鏈表

         定義  type Student struct{

         Name sring

         next * Student

         prevn * Student

}

如果有兩個指針分別指向前一個節點和后一個節點,我們叫做雙鏈表

 

二叉樹

定義:

         type Student struct{

         Name string

         left * Student

         right *Student

}

如果每個節點有兩個指針分別用來指向左子樹和右子樹,我們把這樣的結構叫做二叉樹

對於二叉樹,要用到廣度優先或者深度優先的遞歸算法

 

 

下面是二叉樹的類型圖,下面的stu2的右邊的孩子也可以是為nil,然后stu3如果沒有孩子就叫做葉子節點,stu02是stu01的子樹

 

代碼:

下面采用遞歸的形式進行遍歷二叉樹,下面是深度優先的原理

如果要采取廣度優先,那么每次遍歷的時候就要把結果放到隊列里面

 

前序遍歷:是從根節點開始遍歷的

 

package main

import(
   "fmt"
)

//聲明二叉樹
type Student struct{
   Name string
   Age int
   Score float32
   left *Student
   right *Student
}


func trans(root *Student){
   if root==nil{
      return
   }

   fmt.Println(root)
   //遞歸遍歷左子樹
   trans(root.left)
   //遞歸然后遍歷右子樹
   trans(root.right)
}

func main(){
   //初始化root定點
   var root *Student=new(Student)
   root.Name="stu01"
   root.Age=18
   root.Score=100
   root.left=nil   //初始化
   root.right=nil

   var left1 *Student=new(Student)
   left1.Name="stu02"
   left1.Age=19
   left1.Score=100
   //把這個節點插入到root的左邊
   root.left=left1

   var right1 *Student=new(Student)
   right1.Name="stu04"
   right1.Age=19
   right1.Score=100
   //把這個節點插入到root的右邊
   root.right=right1

   var left02 *Student=new(Student)
   left02.Name="stu03"
   left02.Age=18
   left02.Score=100
   //把這個節點插入到left1的左邊
   left1.left=left02

   trans(root)
}


/*
下面結果分別是,Name Age Score 然后左邊和右邊的地址
&{stu01 18 100 0xc042082090 0xc0420820c0}  
&{stu02 19 100 0xc0420820f0 <nil>}
&{stu03 18 100 <nil> <nil>}
&{stu04 19 100 <nil> <nil>}
*/
中序遍歷:先遍歷左子樹,然后遍歷根節點,然后遍歷右節點
package main

import(
   "fmt"
)

//聲明二叉樹
type Student struct{
   Name string
   Age int
   Score float32
   left *Student
   right *Student
}


func trans(root *Student){
   if root==nil{
      return
   }

   //中序遍歷
   trans(root.left)
   fmt.Println(root)
   trans(root.right)
   /*
   結果
   &{stu03 18 100 <nil> <nil>}
   &{stu02 19 100 0xc0420820f0 <nil>}
   &{stu01 18 100 0xc042082090 0xc0420820c0}
   &{stu04 19 100 <nil> <nil>}
   */
}

func main(){
   //初始化root定點
   var root *Student=new(Student)
   root.Name="stu01"
   root.Age=18
   root.Score=100
   root.left=nil   //初始化
   root.right=nil

   var left1 *Student=new(Student)
   left1.Name="stu02"
   left1.Age=19
   left1.Score=100
   //把這個節點插入到root的左邊
   root.left=left1

   var right1 *Student=new(Student)
   right1.Name="stu04"
   right1.Age=19
   right1.Score=100
   //把這個節點插入到root的右邊
   root.right=right1

   var left02 *Student=new(Student)
   left02.Name="stu03"
   left02.Age=18
   left02.Score=100
   //把這個節點插入到left1的左邊
   left1.left=left02

   trans(root)
}

后序遍歷:首先遍歷左子樹,然后遍歷右子樹,最后遍歷根節點
package main

import(
   "fmt"
)

//聲明二叉樹
type Student struct{
   Name string
   Age int
   Score float32
   left *Student
   right *Student
}


func trans(root *Student){
   if root==nil{
      return
   }

   //后序遍歷
   trans(root.left)
   trans(root.right)
   fmt.Println(root)
   
   /*
   結果
   &{stu03 18 100 <nil> <nil>}
   &{stu02 19 100 0xc04206e0f0 <nil>}
   &{stu04 19 100 <nil> <nil>}
   &{stu01 18 100 0xc04206e090 0xc04206e0c0}
   */
}

func main(){
   //初始化root定點
   var root *Student=new(Student)
   root.Name="stu01"
   root.Age=18
   root.Score=100
   root.left=nil   //初始化
   root.right=nil

   var left1 *Student=new(Student)
   left1.Name="stu02"
   left1.Age=19
   left1.Score=100
   //把這個節點插入到root的左邊
   root.left=left1

   var right1 *Student=new(Student)
   right1.Name="stu04"
   right1.Age=19
   right1.Score=100
   //把這個節點插入到root的右邊
   root.right=right1

   var left02 *Student=new(Student)
   left02.Name="stu03"
   left02.Age=18
   left02.Score=100
   //把這個節點插入到left1的左邊
   left1.left=left02

   trans(root)
}
View Code

結構體與方法

 

結構體是用戶單獨定義的類型,不能和其他類型進行強制轉換

type Student struct{

         Number int

}

type Stu Student  //alias  別名   type 變量  類型 這個是定義類型的別名

var a Student

a=Student{30}

var b Stu

a=b   //這樣賦值錯誤

a=Student(b)  //這樣才可以

上面這兩個Stu和Student是別名關系,但是這兩個字段一樣,並不是同一個類型,因為是type定義的

如:

 

package main

import (
   "fmt"

)

type integer int

func main(){
   
   //賦值給誰呢么類型,就要強制轉換成什么類型
   var i integer=1000
   fmt.Println(i)
   var j int=100
   //這里i是自定義的類型, j是int類型,所以賦值的時候要強制轉換,如下
   j=int(i)              //i如果賦值給j應該強制轉換為int類型
   i=integer(j)        //j如果想復制給i必須轉換為integer類型
   fmt.Println(i)
   fmt.Println(j)
}

工廠模式

golang中的struct沒有構造函數,一般可以使用工廠模式來解決這個問題

Package model

type student Struct{

Name string

Age  int

}

 

func NewStudent(name string,age int)*Student{

         return &Student{   //創建實例

Name:name

Age:age

}

}

 

Package main

S:new(student)

S:model.NewStudent(“tony”,20)

再次強調

make用來創建map,slice ,channel

new 用來創建值類型

 

 

struct中的tag

我們可以為strct中的每一個字段,協商一個tag,這個tag可以通過反射機制獲取到,最常用的場景就是json序列化和反序列化

type student struct{

         Name string  “this is name field” //每個字段寫一個說明,作為這個字段的描述

         Age int      “this is age field”

}

 

json打包

json.Marshal()

注意:

json打包的時候,

1、必須要把結構體中的字段大寫,才可以

下面是程序聲明打包初始化的兩種方式

 

package main

import(
   "encoding/json"
   "fmt"

)

type Student struct{
   Name string  `json:"Student_name"`
   age int      `json:"student_age"`
   score int    `json:score`
}

func main(){
	//聲明
   var stu Student=Student{
      Name:"stu01",
      age:10,
      score:100,
   }
   data,err:=json.Marshal(stu) //打包,返回值為byte
   if err!=nil{
      fmt.Println("json encode stu faild,err",err)
      return
   }
   fmt.Println(string(data))   //把byte轉化成string
}
//{"Student_name":"stu01"}
也可以下面的方式書寫
package main

import(
   "encoding/json"
   "fmt"

)

type Student struct{
   Name string  `json:"Student_name"`
   age int      `json:"student_age"`
   score int    `json:score`
}

func main(){
   //初始化
   var stu *Student=new(Student)
      stu.Name="stu01"
      
   data,err:=json.Marshal(stu) //打包,返回值為byte
   if err!=nil{
      fmt.Println("json encode stu faild,err",err)
      return
   }
   fmt.Println(string(data))   //把byte轉化成string
}
//{"Student_name":"stu01"}

匿名字段

 

結構體 中字段可以沒有名字,叫做匿名字段

type Car struct{

         Name string

         Age int

}

 

type Train struct{

         Car                //匿名字段

         Start time.Time      //有名字段

         int                //匿名字段

}

匿名字段要怎么訪問呢?

 

package main

import (
   "fmt"
   "time"
)

type Cart struct{
   name string
   age int
}

type Train struct{
   Cart
   int
   strt time.Time
}

func main(){
   var t Train
   //正規寫法
   t.Cart.name="001"
   t.Cart.age=11
   //上面的正規寫法可以縮寫成下面的寫法
   t.name="001"
   t.age=11
   t.int=200
   fmt.Println(t)
}

匿名字段沖突處理

 

對於上面的1這里有優先原則:

縮寫形式,如果有兩個結構體中有相同的字段,會優先找本身的字段

對於上面的2,必須要手動的指定某個字段才可以,不然會報錯

 

方法:

golang中的方法是作用在特定類型的變量上,因此自定義類型,都可以有方法,而不僅僅是struct

定義: func (recevier type ) methodName(參數列表)(返回值列表){}

package main

import(
   "fmt"
)

type Student struct{
   Name string
   Age int
   Score int
   sex int
}

func (p *Student) init(name string,age int,){
   p.Name=name
   p.Age=age
   fmt.Println(p)
}

func (p Student) get() Student{
   return p
}

func main(){
   var stu Student
   //由於這里傳遞指針才可以,正規寫法應該是下面
   (&stu).init("stu",10)
   //但是由於go做了優化,只有在結構體方法中才可以用下面的方法
   stu.init("stu",10)

   stu1:=stu.get()
   fmt.Println(stu1)
}
/*
&{stu 10 0 0}
&{stu 10 0 0}
{stu 10 0 0}
*/

方法的調用這里需要注意兩點
1、任何自定義類型都有方法
2、在注意調用的時候的方法,注意指針才改變值
package main

import (
   "fmt"
)

type integer int

func (p integer)print(){
   fmt.Println(p)
}

//這里由於傳遞的是副本,所以無法改變值
func (p integer)set(b integer){
   p=b
}


//這里直接傳遞的指針,所以可以改變
func (p *integer)get(b integer){
   *p=b
}

func main(){
   var a integer
   a=100
   a.print()
   a.set(1000)
   a.print()
   
   //下面是(&a).get的縮寫形式
   a.get(1000)
   a.print()
}

  

方法的調用

type A struct{

         a int

}

func (this A)test(){

         fmt.Println(this.a)

}

 

var t A

t.test()

上面的this就是下面的t,通過上面方法中的參數this.A就能獲取當前結構體中的實例

 

方法和函數的區別:

1)函數調用 :function(variable,參數列表)

2)‘方法 variable.function(參數列表)

 

指針receiver vs值receiver

 本質上和函數的值傳遞和地址傳遞是一樣的

方法的訪問控制,通過大小寫控制

繼承

如果一個struct潛逃了另一個匿名結構體,那么這個結構可以直接訪問匿名結構體的方法,從而實現了繼承

如:

 

package main

import (
   "fmt"
   "time"
)

type Cart struct{
   name string
   age int
}

type Train struct{
   Cart
   int
   strt time.Time
   age int
}

func main(){
   var t Train
   //正規寫法
   t.Cart.name="001"
   t.Cart.age=11
   //上面的正規寫法可以縮寫成下面的寫法
   t.name="001"
   t.age=11
   t.int=200
   fmt.Println(t)
}
這里的Train繼承了Cart,Cart為父類,然后Train里面有Cart的所有的方法

下面是方法的繼承

 

package main

import (
   "fmt"
)

type Car struct{
   weight int
   name string
}

func (p *Car) Run(){
   fmt.Println("running")
}

type Bike struct{
   Car
   lunzi int
}

type Train struct{
   Car
}

func main(){
   var a Bike
   a.weight=100
   a.name="bike"
   a.lunzi=2

   fmt.Println(a)  //{{100 bike} 2}
   a.Run()         //running
 
   var b Train
   b.weight=1000
   b.name="train"
   b.Run()         //running
}
這里a和b都繼承了Car父類中的Run方法

總結,匿名函數可以繼承字段也可以繼承方法

組合和匿名函數

如果一個struct嵌套了另一個匿名結構體,那么這個結構體可以直接訪問匿名結構體的方法,從而實現了繼承

如果一個struct嵌套了另一個有名結構體,那么這個模式就叫做組合(一個結構體嵌套另一個結構體)

也可以說匿名字段是特殊的組合

 

package main

import (
   "fmt"
)

type Car struct{
   weight int
   name string
}

func (p *Car) Run(){
   fmt.Println("running")
}

type Train struct{
   c Car
}

func main(){
   var b Train
   b.c.weight=1000
   b.c.name="train"
   b.c.Run()         //running
}
如上就是組合

 

多重繼承

如果一個struct嵌套了多個匿名結構體,那么這個結構可以直接訪問多個匿名結構體的方法,從而實現了多重繼承

如果沖突的話,就需要帶上結構體的名字來訪問

 

實現String()  這是一個接口

如果一個變量實現了String()這個方法,那么fmt.Println默認會調用變量String()進行輸出

package main

import (
   "fmt"
)

type Cart struct{
   weight int
   name string
}

type Train struct{
   Cart
}

func (p *Cart) Run(){
   fmt.Println("running")
}

func (p *Train)String() string{
   str:=fmt.Sprintf("name=[%s] weight=[%d]",p.name,p.weight)
   return str
}

func main(){
   var b Train
   b.weight=100
   b.name="train"
   b.Run()
   //這個是字符串的接口所以需要格式化才會調用這個接口,這個是指針型的
   fmt.Printf("%s",&b)
}

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM