8.1 概述
對(duì)于面向?qū)ο缶幊痰闹С諫o 語(yǔ)言設(shè)計(jì)得非常簡(jiǎn)潔而優(yōu)雅。因?yàn)椋?Go語(yǔ)言并沒(méi)有沿襲傳統(tǒng)面向?qū)ο缶幊讨械闹T多概念,比如繼承(不支持繼承,盡管匿名字段的內(nèi)存布局和行為類似繼承,但它并不是繼承)、虛函數(shù)、構(gòu)造函數(shù)和析構(gòu)函數(shù)、隱藏的this指針等。
盡管Go語(yǔ)言中沒(méi)有封裝、繼承、多態(tài)這些概念,但同樣通過(guò)別的方式實(shí)現(xiàn)這些特性:
封裝:通過(guò)方法實(shí)現(xiàn)
繼承:通過(guò)匿名字段實(shí)現(xiàn)
多態(tài):通過(guò)接口實(shí)現(xiàn)
8.2 匿名組合
8.2.1 匿名字段
一般情況下,定義結(jié)構(gòu)體的時(shí)候是字段名與其類型一一對(duì)應(yīng),實(shí)際上Go支持只提供類型,而不寫(xiě)字段名的方式,也就是匿名字段,也稱為嵌入字段。
當(dāng)匿名字段也是一個(gè)結(jié)構(gòu)體的時(shí)候,那么這個(gè)結(jié)構(gòu)體所擁有的全部字段都被隱式地引入了當(dāng)前定義的這個(gè)結(jié)構(gòu)體。
//人
type Person struct {
name string
sex byte
age int
}
//學(xué)生
type Student struct {
Person // 匿名字段,那么默認(rèn)Student就包含了Person的所有字段
id int
addr string
}
8.2.2 初始化
//人
type Person struct {
name string
sex byte
age int
}
//學(xué)生
type Student struct {
Person // 匿名字段,那么默認(rèn)Student就包含了Person的所有字段
id int
addr string
}
func main() {
//順序初始化
s1 := Student{Person{"mike", 'm', 18}, 1, "sz"}
//s1 = {Person:{name:mike sex:109 age:18} id:1 addr:sz}
fmt.Printf("s1 = %+v\n", s1)
//s2 := Student{"mike", 'm', 18, 1, "sz"} //err
//部分成員初始化1
s3 := Student{Person: Person{"lily", 'f', 19}, id: 2}
//s3 = {Person:{name:lily sex:102 age:19} id:2 addr:}
fmt.Printf("s3 = %+v\n", s3)
//部分成員初始化2
s4 := Student{Person: Person{name: "tom"}, id: 3}
//s4 = {Person:{name:tom sex:0 age:0} id:3 addr:}
fmt.Printf("s4 = %+v\n", s4)
}
8.2.3 成員的操作
var s1 Student //變量聲明
//給成員賦值
s1.name = "mike" //等價(jià)于 s1.Person.name = "mike"
s1.sex = 'm'
s1.age = 18
s1.id = 1
s1.addr = "sz"
fmt.Println(s1) //{{mike 109 18} 1 sz}
var s2 Student //變量聲明
s2.Person = Person{"lily", 'f', 19}
s2.id = 2
s2.addr = "bj"
fmt.Println(s2) //{{lily 102 19} 2 bj}
8.2.4 同名字段
//人
type Person struct {
name string
sex byte
age int
}
//學(xué)生
type Student struct {
Person // 匿名字段,那么默認(rèn)Student就包含了Person的所有字段
id int
addr string
name string //和Person中的name同名
}
func main() {
var s Student //變量聲明
//給Student的name,還是給Person賦值?
s.name = "mike"
//{Person:{name: sex:0 age:0} id:0 addr: name:mike}
fmt.Printf("%+v\n", s)
//默認(rèn)只會(huì)給最外層的成員賦值
//給匿名同名成員賦值,需要顯示調(diào)用
s.Person.name = "yoyo"
//Person:{name:yoyo sex:0 age:0} id:0 addr: name:mike}
fmt.Printf("%+v\n", s)
}
8.2.5 其它匿名字段
8.2.5.1 非結(jié)構(gòu)體類型
所有的內(nèi)置類型和自定義類型都是可以作為匿名字段的:
type mystr string //自定義類型
type Person struct {
name string
sex byte
age int
}
type Student struct {
Person // 匿名字段,結(jié)構(gòu)體類型
int // 匿名字段,內(nèi)置類型
mystr // 匿名字段,自定義類型
}
func main() {
//初始化
s1 := Student{Person{"mike", 'm', 18}, 1, "bj"}
//{Person:{name:mike sex:109 age:18} int:1 mystr:bj}
fmt.Printf("%+v\n", s1)
//成員的操作,打印結(jié)果:mike, m, 18, 1, bj
fmt.Printf("%s, %c, %d, %d, %s\n", s1.name, s1.sex, s1.age, s1.int, s1.mystr)
}
8.2.5.2 結(jié)構(gòu)體指針類型
type Person struct { //人
name string
sex byte
age int
}
type Student struct { //學(xué)生
*Person // 匿名字段,結(jié)構(gòu)體指針類型
id int
addr string
}
func main() {
//初始化
s1 := Student{&Person{"mike", 'm', 18}, 1, "bj"}
//{Person:0xc0420023e0 id:1 addr:bj}
fmt.Printf("%+v\n", s1)
//mike, m, 18
fmt.Printf("%s, %c, %d\n", s1.name, s1.sex, s1.age)
//聲明變量
var s2 Student
s2.Person = new(Person) //分配空間
s2.name = "yoyo"
s2.sex = 'f'
s2.age = 20
s2.id = 2
s2.addr = "sz"
//yoyo 102 20 2 20
fmt.Println(s2.name, s2.sex, s2.age, s2.id, s2.age)
}
8.3 方法
8.3.1 概述
在面向?qū)ο缶幊讨校粋€(gè)對(duì)象其實(shí)也就是一個(gè)簡(jiǎn)單的值或者一個(gè)變量,在這個(gè)對(duì)象中會(huì)包含一些函數(shù),這種帶有接收者的函數(shù),我們稱為方法(method)。 本質(zhì)上,一個(gè)方法則是一個(gè)和特殊類型關(guān)聯(lián)的函數(shù)。
一個(gè)面向?qū)ο蟮某绦驎?huì)用方法來(lái)表達(dá)其屬性和對(duì)應(yīng)的操作,這樣使用這個(gè)對(duì)象的用戶就不需要直接去操作對(duì)象,而是借助方法來(lái)做這些事情。
在Go語(yǔ)言中,可以給任意自定義類型(包括內(nèi)置類型,但不包括指針類型)添加相應(yīng)的方法。
?法總是綁定對(duì)象實(shí)例,并隱式將實(shí)例作為第?實(shí)參 (receiver),方法的語(yǔ)法如下:
func (receiver ReceiverType) funcName(parameters) (results)
參數(shù) receiver 可任意命名。如?法中未曾使?,可省略參數(shù)名。
參數(shù) receiver 類型可以是 T 或 *T?;愋?T 不能是接?或指針。
不支持重載方法,也就是說(shuō),不能定義名字相同但是不同參數(shù)的方法。
8.3.2 為類型添加方法
8.3.2.1 基礎(chǔ)類型作為接收者
type MyInt int //自定義類型,給int改名為MyInt
//在函數(shù)定義時(shí),在其名字之前放上一個(gè)變量,即是一個(gè)方法
func (a MyInt) Add(b MyInt) MyInt { //面向?qū)ο? return a + b
}
//傳統(tǒng)方式的定義
func Add(a, b MyInt) MyInt { //面向過(guò)程
return a + b
}
func main() {
var a MyInt = 1
var b MyInt = 1
//調(diào)用func (a MyInt) Add(b MyInt)
fmt.Println("a.Add(b) = ", a.Add(b)) //a.Add(b) = 2
//調(diào)用func Add(a, b MyInt)
fmt.Println("Add(a, b) = ", Add(a, b)) //Add(a, b) = 2
}
通過(guò)上面的例子可以看出,面向?qū)ο笾皇菗Q了一種語(yǔ)法形式來(lái)表達(dá)。方法是函數(shù)的語(yǔ)法糖,因?yàn)閞eceiver其實(shí)就是方法所接收的第1個(gè)參數(shù)。
注意:雖然方法的名字一模一樣,但是如果接收者不一樣,那么方法就不一樣。
8.3.2.2 結(jié)構(gòu)體作為接收者
方法里面可以訪問(wèn)接收者的字段,調(diào)用方法通過(guò)點(diǎn)( . )訪問(wèn),就像struct里面訪問(wèn)字段一樣:
type Person struct {
name string
sex byte
age int
}
func (p Person) PrintInfo() { //給Person添加方法
fmt.Println(p.name, p.sex, p.age)
}
func main() {
p := Person{"mike", 'm', 18} //初始化
p.PrintInfo() //調(diào)用func (p Person) PrintInfo()
}
8.3.3 值語(yǔ)義和引用語(yǔ)義
type Person struct {
name string
sex byte
age int
}
//指針作為接收者,引用語(yǔ)義
func (p *Person) SetInfoPointer() {
//給成員賦值
(*p).name = "yoyo"
p.sex = 'f'
p.age = 22
}
//值作為接收者,值語(yǔ)義
func (p Person) SetInfoValue() {
//給成員賦值
p.name = "yoyo"
p.sex = 'f'
p.age = 22
}
func main() {
//指針作為接收者,引用語(yǔ)義
p1 := Person{"mike", 'm', 18} //初始化
fmt.Println("函數(shù)調(diào)用前 = ", p1) //函數(shù)調(diào)用前 = {mike 109 18}
(&p1).SetInfoPointer()
fmt.Println("函數(shù)調(diào)用后 = ", p1) //函數(shù)調(diào)用后 = {yoyo 102 22}
fmt.Println("==========================")
p2 := Person{"mike", 'm', 18} //初始化
//值作為接收者,值語(yǔ)義
fmt.Println("函數(shù)調(diào)用前 = ", p2) //函數(shù)調(diào)用前 = {mike 109 18}
p2.SetInfoValue()
fmt.Println("函數(shù)調(diào)用后 = ", p2) //函數(shù)調(diào)用后 = {mike 109 18}
}
8.3.4 方法集
類型的方法集是指可以被該類型的值調(diào)用的所有方法的集合。
用實(shí)例實(shí)例 value 和 pointer 調(diào)用方法(含匿名字段)不受?法集約束,編譯器編總是查找全部方法,并自動(dòng)轉(zhuǎn)換 receiver 實(shí)參。
8.3.4.1 類型 *T 方法集
一個(gè)指向自定義類型的值的指針,它的方法集由該類型定義的所有方法組成,無(wú)論這些方法接受的是一個(gè)值還是一個(gè)指針。
如果在指針上調(diào)用一個(gè)接受值的方法,Go語(yǔ)言會(huì)聰明地將該指針解引用,并將指針?biāo)傅牡讓又底鳛榉椒ǖ慕邮照摺?/p>
類型 *T ?法集包含全部 receiver T + *T ?法:
type Person struct {
name string
sex byte
age int
}
//指針作為接收者,引用語(yǔ)義
func (p *Person) SetInfoPointer() {
(*p).name = "yoyo"
p.sex = 'f'
p.age = 22
}
//值作為接收者,值語(yǔ)義
func (p Person) SetInfoValue() {
p.name = "xxx"
p.sex = 'm'
p.age = 33
}
func main() {
//p 為指針類型
var p *Person = &Person{"mike", 'm', 18}
p.SetInfoPointer() //func (p) SetInfoPointer()
p.SetInfoValue() //func (*p) SetInfoValue()
(*p).SetInfoValue() //func (*p) SetInfoValue()
}
8.3.4.2 類型 T 方法集
一個(gè)自定義類型值的方法集則由為該類型定義的接收者類型為值類型的方法組成,但是不包含那些接收者類型為指針的方法。
但這種限制通常并不像這里所說(shuō)的那樣,因?yàn)槿绻覀冎挥幸粋€(gè)值,仍然可以調(diào)用一個(gè)接收者為指針類型的方法,這可以借助于Go語(yǔ)言傳值的地址能力實(shí)現(xiàn)。
type Person struct {
name string
sex byte
age int
}
//指針作為接收者,引用語(yǔ)義
func (p *Person) SetInfoPointer() {
(*p).name = "yoyo"
p.sex = 'f'
p.age = 22
}
//值作為接收者,值語(yǔ)義
func (p Person) SetInfoValue() {
p.name = "xxx"
p.sex = 'm'
p.age = 33
}
func main() {
//p 為普通值類型
var p Person = Person{"mike", 'm', 18}
(&p).SetInfoPointer() //func (&p) SetInfoPointer()
p.SetInfoPointer() //func (&p) SetInfoPointer()
p.SetInfoValue() //func (p) SetInfoValue()
(&p).SetInfoValue() //func (*&p) SetInfoValue()
}
8.3.5 匿名字段
8.3.5.1 方法的繼承
如果匿名字段實(shí)現(xiàn)了一個(gè)方法,那么包含這個(gè)匿名字段的struct也能調(diào)用該方法。
type Person struct {
name string
sex byte
age int
}
//Person定義了方法
func (p *Person) PrintInfo() {
fmt.Printf("%s,%c,%d\n", p.name, p.sex, p.age)
}
type Student struct {
Person // 匿名字段,那么Student包含了Person的所有字段
id int
addr string
}
func main() {
p := Person{"mike", 'm', 18}
p.PrintInfo()
s := Student{Person{"yoyo", 'f', 20}, 2, "sz"}
s.PrintInfo()
}
8.3.5.2 方法的重寫(xiě)
type Person struct {
name string
sex byte
age int
}
//Person定義了方法
func (p *Person) PrintInfo() {
fmt.Printf("Person: %s,%c,%d\n", p.name, p.sex, p.age)
}
type Student struct {
Person // 匿名字段,那么Student包含了Person的所有字段
id int
addr string
}
//Student定義了方法
func (s *Student) PrintInfo() {
fmt.Printf("Student:%s,%c,%d\n", s.name, s.sex, s.age)
}
func main() {
p := Person{"mike", 'm', 18}
p.PrintInfo() //Person: mike,m,18
s := Student{Person{"yoyo", 'f', 20}, 2, "sz"}
s.PrintInfo() //Student:yoyo,f,20
s.Person.PrintInfo() //Person: yoyo,f,20
}
8.3.6 表達(dá)式
類似于我們可以對(duì)函數(shù)進(jìn)行賦值和傳遞一樣,方法也可以進(jìn)行賦值和傳遞。
根據(jù)調(diào)用者不同,方法分為兩種表現(xiàn)形式:方法值和方法表達(dá)式。兩者都可像普通函數(shù)那樣賦值和傳參,區(qū)別在于方法值綁定實(shí)例,?方法表達(dá)式則須顯式傳參。
8.3.6.1 方法值
type Person struct {
name string
sex byte
age int
}
func (p *Person) PrintInfoPointer() {
fmt.Printf("%p, %v\n", p, p)
}
func (p Person) PrintInfoValue() {
fmt.Printf("%p, %v\n", &p, p)
}
func main() {
p := Person{"mike", 'm', 18}
p.PrintInfoPointer() //0xc0420023e0, &{mike 109 18}
pFunc1 := p.PrintInfoPointer //方法值,隱式傳遞 receiver
pFunc1() //0xc0420023e0, &{mike 109 18}
pFunc2 := p.PrintInfoValue
pFunc2() //0xc042048420, {mike 109 18}
}
8.3.6.2 方法表達(dá)式
type Person struct {
name string
sex byte
age int
}
func (p *Person) PrintInfoPointer() {
fmt.Printf("%p, %v\n", p, p)
}
func (p Person) PrintInfoValue() {
fmt.Printf("%p, %v\n", &p, p)
}
func main() {
p := Person{"mike", 'm', 18}
p.PrintInfoPointer() //0xc0420023e0, &{mike 109 18}
//方法表達(dá)式, 須顯式傳參
//func pFunc1(p *Person))
pFunc1 := (*Person).PrintInfoPointer
pFunc1(&p) //0xc0420023e0, &{mike 109 18}
pFunc2 := Person.PrintInfoValue
pFunc2(p) //0xc042002460, {mike 109 18}
}
8.4 接口
8.4.1 概述
在Go語(yǔ)言中,接口(interface)是一個(gè)自定義類型,接口類型具體描述了一系列方法的集合。
接口類型是一種抽象的類型,它不會(huì)暴露出它所代表的對(duì)象的內(nèi)部值的結(jié)構(gòu)和這個(gè)對(duì)象支持的基礎(chǔ)操作的集合,它們只會(huì)展示出它們自己的方法。因此接口類型不能將其實(shí)例化。
Go通過(guò)接口實(shí)現(xiàn)了鴨子類型(duck-typing):“當(dāng)看到一只鳥(niǎo)走起來(lái)像鴨子、游泳起來(lái)像鴨子、叫起來(lái)也像鴨子,那么這只鳥(niǎo)就可以被稱為鴨子”。我們并不關(guān)心對(duì)象是什么類型,到底是不是鴨子,只關(guān)心行為。
8.4.2 接口的使用
8.4.2.1 接口定義
type Humaner interface {
SayHi()
}
接?命名習(xí)慣以 er 結(jié)尾
接口只有方法聲明,沒(méi)有實(shí)現(xiàn),沒(méi)有數(shù)據(jù)字段
接口可以匿名嵌入其它接口,或嵌入到結(jié)構(gòu)中
8.4.2.2 接口實(shí)現(xiàn)
接口是用來(lái)定義行為的類型。這些被定義的行為不由接口直接實(shí)現(xiàn),而是通過(guò)方法由用戶定義的類型實(shí)現(xiàn),一個(gè)實(shí)現(xiàn)了這些方法的具體類型是這個(gè)接口類型的實(shí)例。
如果用戶定義的類型實(shí)現(xiàn)了某個(gè)接口類型聲明的一組方法,那么這個(gè)用戶定義的類型的值就可以賦給這個(gè)接口類型的值。這個(gè)賦值會(huì)把用戶定義的類型的值存入接口類型的值。
type Humaner interface {
SayHi()
}
type Student struct { //學(xué)生
name string
score float64
}
//Student實(shí)現(xiàn)SayHi()方法
func (s *Student) SayHi() {
fmt.Printf("Student[%s, %f] say hi!!\n", s.name, s.score)
}
type Teacher struct { //老師
name string
group string
}
//Teacher實(shí)現(xiàn)SayHi()方法
func (t *Teacher) SayHi() {
fmt.Printf("Teacher[%s, %s] say hi!!\n", t.name, t.group)
}
type MyStr string
//MyStr實(shí)現(xiàn)SayHi()方法
func (str MyStr) SayHi() {
fmt.Printf("MyStr[%s] say hi!!\n", str)
}
//普通函數(shù),參數(shù)為Humaner類型的變量i
func WhoSayHi(i Humaner) {
i.SayHi()
}
func main() {
s := &Student{"mike", 88.88}
t := &Teacher{"yoyo", "Go語(yǔ)言"}
var tmp MyStr = "測(cè)試"
s.SayHi() //Student[mike, 88.880000] say hi!!
t.SayHi() //Teacher[yoyo, Go語(yǔ)言] say hi!!
tmp.SayHi() //MyStr[測(cè)試] say hi!!
//多態(tài),調(diào)用同一接口,不同表現(xiàn)
WhoSayHi(s) //Student[mike, 88.880000] say hi!!
WhoSayHi(t) //Teacher[yoyo, Go語(yǔ)言] say hi!!
WhoSayHi(tmp) //MyStr[測(cè)試] say hi!!
x := make([]Humaner, 3)
//這三個(gè)都是不同類型的元素,但是他們實(shí)現(xiàn)了interface同一個(gè)接口
x[0], x[1], x[2] = s, t, tmp
for _, value := range x {
value.SayHi()
}
/*
Student[mike, 88.880000] say hi!!
Teacher[yoyo, Go語(yǔ)言] say hi!!
MyStr[測(cè)試] say hi!!
*/
}
通過(guò)上面的代碼,你會(huì)發(fā)現(xiàn)接口就是一組抽象方法的集合,它必須由其他非接口類型實(shí)現(xiàn),而不能自我實(shí)現(xiàn)。
8.4.3 接口組合
8.4.3.1 接口嵌入
如果一個(gè)interface1作為interface2的一個(gè)嵌入字段,那么interface2隱式的包含了interface1里面的方法。
type Humaner interface {
SayHi()
}
type Personer interface {
Humaner //這里想寫(xiě)了SayHi()一樣
Sing(lyrics string)
}
type Student struct { //學(xué)生
name string
score float64
}
//Student實(shí)現(xiàn)SayHi()方法
func (s *Student) SayHi() {
fmt.Printf("Student[%s, %f] say hi!!\n", s.name, s.score)
}
//Student實(shí)現(xiàn)Sing()方法
func (s *Student) Sing(lyrics string) {
fmt.Printf("Student sing[%s]!!\n", lyrics)
}
func main() {
s := &Student{"mike", 88.88}
var i2 Personer
i2 = s
i2.SayHi() //Student[mike, 88.880000] say hi!!
i2.Sing("學(xué)生哥") //Student sing[學(xué)生哥]!!
}
8.4.3.2 接口轉(zhuǎn)換
超集接?對(duì)象可轉(zhuǎn)換為?集接?,反之出錯(cuò):
type Humaner interface {
SayHi()
}
type Personer interface {
Humaner //這里像寫(xiě)了SayHi()一樣
Sing(lyrics string)
}
type Student struct { //學(xué)生
name string
score float64
}
//Student實(shí)現(xiàn)SayHi()方法
func (s *Student) SayHi() {
fmt.Printf("Student[%s, %f] say hi!!\n", s.name, s.score)
}
//Student實(shí)現(xiàn)Sing()方法
func (s *Student) Sing(lyrics string) {
fmt.Printf("Student sing[%s]!!\n", lyrics)
}
func main() {
//var i1 Humaner = &Student{"mike", 88.88}
//var i2 Personer = i1 //err
//Personer為超集,Humaner為子集
var i1 Personer = &Student{"mike", 88.88}
var i2 Humaner = i1
i2.SayHi() //Student[mike, 88.880000] say hi!!
}
8.4.4 空接口
空接口(interface{})不包含任何的方法,正因?yàn)槿绱?,所有的類型都?shí)現(xiàn)了空接口,因此空接口可以存儲(chǔ)任意類型的數(shù)值。它有點(diǎn)類似于C語(yǔ)言的void *類型。
var v1 interface{} = 1 // 將int類型賦值給interface{}
var v2 interface{} = "abc" // 將string類型賦值給interface{}
var v3 interface{} = &v2 // 將*interface{}類型賦值給interface{}
var v4 interface{} = struct{ X int }{1}
var v5 interface{} = &struct{ X int }{1}
當(dāng)函數(shù)可以接受任意的對(duì)象實(shí)例時(shí),我們會(huì)將其聲明為interface{},最典型的例子是標(biāo)準(zhǔn)庫(kù)fmt中PrintXXX系列的函數(shù),例如:
func Printf(fmt string, args ...interface{})
func Println(args ...interface{})
8.4.5 類型查詢
我們知道interface的變量里面可以存儲(chǔ)任意類型的數(shù)值(該類型實(shí)現(xiàn)了interface)。那么我們?cè)趺捶聪蛑肋@個(gè)變量里面實(shí)際保存了的是哪個(gè)類型的對(duì)象呢?目前常用的有兩種方法:
comma-ok斷言
switch測(cè)試
8.4.5.1 comma-ok斷言
Go語(yǔ)言里面有一個(gè)語(yǔ)法,可以直接判斷是否是該類型的變量: value, ok = element.(T),這里value就是變量的值,ok是一個(gè)bool類型,element是interface變量,T是斷言的類型。
如果element里面確實(shí)存儲(chǔ)了T類型的數(shù)值,那么ok返回true,否則返回false。
示例代碼:
type Element interface{}
type Person struct {
name string
age int
}
func main() {
list := make([]Element, 3)
list[0] = 1 // an int
list[1] = "Hello" // a string
list[2] = Person{"mike", 18}
for index, element := range list {
if value, ok := element.(int); ok {
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
} else if value, ok := element.(string); ok {
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
} else if value, ok := element.(Person); ok {
fmt.Printf("list[%d] is a Person and its value is [%s, %d]\n", index, value.name, value.age)
} else {
fmt.Printf("list[%d] is of a different type\n", index)
}
}
/* 打印結(jié)果:
list[0] is an int and its value is 1
list[1] is a string and its value is Hello
list[2] is a Person and its value is [mike, 18]
*/
}
8.4.5.2 switch測(cè)試
type Element interface{}
type Person struct {
name string
age int
}
func main() {
list := make([]Element, 3)
list[0] = 1 //an int
list[1] = "Hello" //a string
list[2] = Person{"mike", 18}
for index, element := range list {
switch value := element.(type) {
case int:
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
case string:
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
case Person:
fmt.Printf("list[%d] is a Person and its value is [%s, %d]\n", index, value.name, value.age)
default:
fmt.Println("list[%d] is of a different type", index)
}
}
}