1.简介
结构是已命名的元素序列,被称为字段,其中每一个元素都有一个名字和类型。 字段名可显示地指定(标识符列表)或隐式地指定(匿名字段)。 在结构中,非空白字段名必须是唯一的。
// 空结构
struct {}
// 带7个字段的结构
type STest struct {
x, y int
u float32
_ float32 // 填充空白字段
_ float64 // 填充空白字段
A *[]int
F func()
}
2.匿名字段
通过有类型而无显式字段名声明的字段为匿名字段,亦称为嵌入式字段或类型的嵌入。 嵌入的类型必须是一个类型名 T 或一个非接口类型的指针 *T, 且 T 本身不能为指针类型。未指定字段名的类型名可以作为字段名。
// 带类型为T1,*T2,P.T3和*P.T4的4个匿名字段的结构
struct {
T1 // 字段名为T1
*T2 // 字段名为T2
P.T3 // 字段名为T3
*P.T4 // 字段名为T4
x, y int // 字段名为x和y
}
以下为非法声明,因为字段名在结构类型中必须是唯一的:
struct {
T // 与匿名字段*T及*P.T相冲突
*T // 与匿名字段T及*P.T相冲突
*P.T // 与匿名字段T及*T相冲突
}
3.已提升的字段与方法
在结构 x 中,假设 f 是匿名字段的字段或者方法,如果 x. f 是合法的,则匿名字段的字段或方法 f 即为已提升的。
已提升的字段除了不能用作复合字面量赋值时的字段名外, 其行为如同结构体的一般字段。比如下面通过复合字面量构造结构体时,匿名字段的字段不能直接使用。
type T struct {
x, y int
}
type Test struct{
name string
T //匿名字段T
}
//错误的使用,报编译错误:unknown field 'x' in struct literal of type Test
test:=Test{name:"dablelv",x:1,y:2}
//错误的使用1,报编译错误:missing type in composite literal
test:=Test{"dablelv",{1,2}}
//正确的使用
test:=Test{"dablelv",T{1,2}}
给定结构类型 S 与名为 T 的类型,若 S 包含一个匿名字段 T 或 *T,则包含在结构 S 方法集中的已提升方法有:接收者为 T 或 *T 的方法。参考如下示例:
package main
import (
"fmt"
)
type T struct {
x, y int
}
func (t T) print(){
t.x=666
fmt.Printf("in print()\n")
}
func (t *T) printByPointer(){
t.x=888
fmt.Printf("in printByPointer()\n")
}
type Test struct{
name string
T
}
func main(){
var test Test
test.print() //Test调用提升方法T.print()
fmt.Printf("%#v\n",test)
test.printByPointer() //Test调用提升方法(*T).printByPointer()
fmt.Printf("%#v\n",test)
(&test).print() //*Test调用提升方法T.print()
fmt.Printf("%#v\n",test)
(&test).printByPointer() //*Test调用提升方法(*T).printByPointer()
fmt.Printf("%#v\n",test)
}
程序输出结果:
in print()
main.Test{name:"", T:main.T{x:0, y:0}}
in printByPointer()
main.Test{name:"", T:main.T{x:888, y:0}}
in print()
main.Test{name:"", T:main.T{x:888, y:0}}
in printByPointer()
main.Test{name:"", T:main.T{x:888, y:0}}
将匿名字段T变为*T,同样可以调用接收者为T 或 *T 的已提升方法。参考代码如下:
type Test struct{
name string
*T
}
func main(){
var test=Test{"",&T{0,0}}
test.print() //Test调用提升方法T.print()
fmt.Printf("%s %d %d\n",test.name,test.x,test.y)
test.printByPointer() //Test调用提升方法(*T).printByPointer()
fmt.Printf("%s %d %d\n",test.name,test.x,test.y)
(&test).print() //*Test调用提升方法T.print()
fmt.Printf("%s %d %d\n",test.name,test.x,test.y)
(&test).printByPointer() //*Test调用提升方法(*T).printByPointer()
fmt.Printf("%s %d %d\n",test.name,test.x,test.y)
}
运行结果:
in print()
0 0
in printByPointer()
888 0
in print()
888 0
in printByPointer()
888 0
4.字段标注
字段声明可后跟一个可选的字符串字面标注,成为字段的属性。 标注可通过反射接口获得,否则就会被忽略。
// 一个对应于时间戳协议缓存的结构.
// 标注字符串定义了协议缓存的字段号.
struct {
microsec uint64 "field 1"
serverIP6 uint64 "field 2"
process string "field 3"
}
参考文献
[1] Go编程语言规范