typeefacestruct{_type*_typedataunsafe.Pointer}type_typestruct{sizeuintptr// type size
ptrdatauintptr// size of memory prefix holding all pointers
hashuint32// hash of type; avoids computation in hash tables
tflagtflag// extra type information flags
alignuint8// alignment of variable with this type
fieldalignuint8// alignment of struct field with this type
kinduint8// enumeration for C
alg*typeAlg// algorithm table
gcdata*byte// garbage collection data
strnameOff// string form
ptrToThistypeOff// type for pointer to this type, may be zero
}
typeifacestruct{tab*itabdataunsafe.Pointer}// layout of Itab known to compilers
// allocated in non-garbage-collected memory
// Needs to be in sync with
// ../cmd/compile/internal/gc/reflect.go:/^func.dumptypestructs.
typeitabstruct{inter*interfacetype_type*_typelink*itabbadint32inhashint32// has this itab been added to hash?
fun[1]uintptr// variable sized
}typeinterfacetypestruct{typ_typepkgpathnamemhdr[]imethod}typeimethodstruct{//这里的 method 只是一种函数声明的抽象,比如 func Print() error
namenameOffityptypeOff}
typeShapeinterface{Area()float32}funcmain(){varsShapefmt.Println("value of s is",s)fmt.Printf("type of s is %T\n",s)}//Output:
valueofsis<nil>typeofsis<nil>
typeNamerinterface{Name()}typeSt1struct{aint}func(st1St1)Name(){fmt.Println(st1.a)}typeSt2struct{}func(St2)Name(){}funcmain(){variNamer=St1{2}fmt.Printf("type is %T\n",i)fmt.Printf("value is %v\n",i)i=St2{}fmt.Printf("type is %T\n",i)fmt.Printf("value is %v\n",i)}//Output:
typeismain.St1valueis{2}typeismain.St2valueis{}
packagemainimport"fmt"typeShaperinterface{Area()float32}typeSquarestruct{sidefloat32}func(sq*Square)Area()float32{returnsq.side*sq.side}typeRectanglestruct{length,widthfloat32}func(rRectangle)Area()float32{returnr.length*r.width}funcmain(){r:=Rectangle{5,3}// Area() of Rectangle needs a value
q:=&Square{5}// Area() of Square needs a pointer
// shapes := []Shaper{Shaper(r), Shaper(q)}
// or shorter
shapes:=[]Shaper{r,q}fmt.Println("Looping through shapes for area ...")forn,_:=rangeshapes{fmt.Println("Shape details: ",shapes[n])fmt.Println("Area of this shape is: ",shapes[n].Area())}}//Output:
Loopingthroughshapesforarea...Shapedetails:{53}Areaofthisshapeis:15Shapedetails:&{5}Areaofthisshapeis:25
1.4 接口嵌套
Go 中的接口可以包含一个或多个其它接口,这相当于直接将这些内嵌接口的方法列举在外层接口中。下例中File接口包含了ReadWrite和Lock接口的所有方法
packagemainimport("fmt""math")typeSquarestruct{sidefloat32}typeCirclestruct{radiusfloat32}typeShaperinterface{Area()float32}funcmain(){varareaIntfShapersq1:=new(Square)sq1.side=5areaIntf=sq1// Is Square the type of areaIntf?
ift,ok:=areaIntf.(*Square);ok{fmt.Printf("The type of areaIntf is: %T\n",t)}ifu,ok:=areaIntf.(*Circle);ok{fmt.Printf("The type of areaIntf is: %T\n",u)}else{fmt.Println("areaIntf does not contain a variable of type Circle")}}func(sq*Square)Area()float32{returnsq.side*sq.side}func(ci*Circle)Area()float32{returnci.radius*ci.radius*math.Pi}//Output:
ThetypeofareaIntfis:*main.SquareareaIntfdoesnotcontainavariableoftypeCircle
类型断言可能失败,为了更安全的使用类型断言,使用如下的方式
1
2
3
4
5
ifv,ok:=varI.(T);ok{// checked type assertion
Process(v)return}// varI is not of type T
由于断言是一个比较的过程,因此需要多次尝试,使用 switch 语句最为简便,不过要求所有 case 语句中列举的类型(nil除外)都必须实现对应的接口。
1
2
3
4
5
6
7
8
9
10
11
12
switcht:=areaIntf.(type){case*Square:fmt.Printf("Type Square %T with value %v\n",t,t)case*Circle:fmt.Printf("Type Circle %T with value %v\n",t,t)casenil:fmt.Printf("nil value: nothing to check?\n")default:fmt.Printf("Unexpected type %T\n",t)}//Output:
TypeSquare*main.Squarewithvalue&{5}
类型断言还有一种反向用法,就是测试它是否实现了某个接口,如下
1
2
3
4
5
6
7
typeStringerinterface{String()string}ifsv,ok:=v.(Stringer);ok{fmt.Printf("v implements String(): %s\n",sv.String())// note: sv, not v
}
所以我们可以意识到,断言格式 varI.(T) 中的 varI 可以是任意变量,T 是任意类型,断言的实质就是将变量转换为 T 类型的值,然后进行比较。
packagemainimport("fmt")typeList[]intfunc(lList)Len()int{returnlen(l)}func(l*List)Append(valint){*l=append(*l,val)}typeAppenderinterface{Append(int)}funcCountInto(aAppender,start,endint){fori:=start;i<=end;i++{a.Append(i)}}typeLenerinterface{Len()int}funcLongEnough(lLener)bool{returnl.Len()*10>42}funcmain(){// A bare value
varlstList// compiler error:
// cannot use lst (type List) as type Appender in argument to CountInto:
// List does not implement Appender (Append method has pointer receiver)
// CountInto(lst, 1, 10)
ifLongEnough(lst){// VALID:Identical receiver type
fmt.Printf("- lst is long enough\n")}// A pointer value
plst:=new(List)CountInto(plst,1,10)//VALID:Identical receiver type
ifLongEnough(plst){// VALID: a *List can be dereferenced for the receiver
fmt.Printf("- plst is long enough\n")}}
packagemainimport("fmt""reflect")funcmain(){varxfloat64=3.4fmt.Println("type:",reflect.TypeOf(x))v:=reflect.ValueOf(x)fmt.Println("value:",v)fmt.Println("type:",v.Type())fmt.Println("kind:",v.Kind())fmt.Println("value:",v.Float())fmt.Println(v.Interface())fmt.Printf("value is %5.2e\n",v.Interface())y:=v.Interface().(float64)fmt.Println(y)}//Output:
type:float64value:3.4type:float64kind:float64value:3.43.4valueis3.40e+003.4
packagemainimport("fmt""reflect")funcmain(){varxfloat64=3.4v:=reflect.ValueOf(x)// setting a value:
// v.SetFloat(3.1415) // Error: will panic: reflect.Value.SetFloat using unaddressable value
fmt.Println("settability of v:",v.CanSet())v=reflect.ValueOf(&x)// Note: take the address of x.
fmt.Println("type of v:",v.Type())fmt.Println("settability of v:",v.CanSet())v=v.Elem()fmt.Println("The Elem of v is: ",v)fmt.Println("settability of v:",v.CanSet())v.SetFloat(3.1415)// this works!
fmt.Println(v.Interface())fmt.Println(v)}//Output:
settabilityofv:falsetypeofv:*float64settabilityofv:falseTheElemofvis:<float64Value>settabilityofv:true3.1415<float64Value>
packagemainimport("fmt""reflect")typeNotknownTypestruct{s1,s2,s3string}func(nNotknownType)String()string{returnn.s1+" - "+n.s2+" - "+n.s3}// variable to investigate:
varsecretinterface{}=NotknownType{"Ada","Go","Oberon"}funcmain(){value:=reflect.ValueOf(secret)// <main.NotknownType Value>
typ:=reflect.TypeOf(secret)// main.NotknownType
// alternative:
//typ := value.Type() // main.NotknownType
fmt.Println(typ)knd:=value.Kind()// struct
fmt.Println(knd)// iterate through the fields of the struct:
fori:=0;i<value.NumField();i++{fmt.Printf("Field %d: %v\n",i,value.Field(i))// error: panic: reflect.Value.SetString using value obtained using unexported field
//value.Field(i).SetString("C#")
}// call the first method, which is String():
results:=value.Method(0).Call(nil)fmt.Println(results)// [Ada - Go - Oberon]
}//Output:
main.NotknownTypestructField0:AdaField1:GoField2:Oberon[Ada-Go-Oberon]
packagemainimport("fmt")typeLogstruct{msgstring}typeCustomerstruct{Namestringlog*Log}funcmain(){c:=new(Customer)c.Name="Barak Obama"c.log=new(Log)c.log.msg="1 - Yes we can!"// shorter
c=&Customer{"Barak Obama",&Log{"1 - Yes we can!"}}// fmt.Println(c) &{Barak Obama 1 - Yes we can!}
c.Log().Add("2 - After me the world will be a better place!")//fmt.Println(c.log)
fmt.Println(c.Log())}func(l*Log)Add(sstring){l.msg+="\n"+s}func(l*Log)String()string{returnl.msg}func(c*Customer)Log()*Log{returnc.log}//Output:
1-Yeswecan!2-Aftermetheworldwillbeabetterplace!
packagemainimport("fmt")typeLogstruct{msgstring}typeCustomerstruct{NamestringLog}funcmain(){c:=&Customer{"Barak Obama",Log{"1 - Yes we can!"}}c.Add("2 - After me the world will be a better place!")fmt.Println(c)}func(l*Log)Add(sstring){l.msg+="\n"+s}func(l*Log)String()string{returnl.msg}func(c*Customer)String()string{returnc.Name+"\nLog:"+fmt.Sprintln(c.Log)}//Output:
BarakObamaLog:{1-Yeswecan!2-Aftermetheworldwillbeabetterplace!}
packagemainimport("fmt")typeCamerastruct{}func(c*Camera)TakeAPicture()string{return"Click"}typePhonestruct{}func(p*Phone)Call()string{return"Ring Ring"}typeCameraPhonestruct{CameraPhone}funcmain(){cp:=new(CameraPhone)fmt.Println("Our new CameraPhone exhibits multiple behaviors...")fmt.Println("It exhibits behavior of a Camera: ",cp.TakeAPicture())fmt.Println("It works like a Phone too: ",cp.Call())}//Output:
OurnewCameraPhoneexhibitsmultiplebehaviors...ItexhibitsbehaviorofaCamera:ClickItworkslikeaPhonetoo:RingRing