结构体
结构体
定义
结构体定义的是数据,与C语言的结构体相同。
结构体定义如下:
type structTypeName struct {
member definition;
member definition;
...
member definition;
}
结构体变量可以有两种声明方式(使用KV方式时,可以仅初始化部分成员):
varName := structTypeName {value1, value2...valuen}
varName := structTypeName { key1: value1, key2: value2..., keyn: valuen}
还有另外一种,通过new来创建特定对象:
varName := new (structTypeName) // 使用默认值初始化
代码范例:
type student struct {
name string
age int
grage int
}
func main() {
s1 := student{"wang", 10, 2}
s2 := new(student)
// s3 := make(student) // cannot make type student
s2.name = "li";
s2.age = 11;
fmt.Println(s1)
fmt.Println(s2)
}
内嵌结构体
内嵌结构体是指将某个结构体,潜入到其他的结构体中,类似于C语言中的struct内部定义struct。
Go语言中不能在struct内部嵌套,而是需要将内嵌的结构体独立声明。
范例,我们需要在student结构体中内嵌other结构体:
type other struct { // 声明other结构体
addr string
}
type student struct {
name string
age int
grage int
other // 内嵌other结构体
}
如果名字没有冲突,可以直接使用内嵌结构体的字段
s1 := student{"wang", 10, 2, other{"JS"}} // 初始化时需要带着内嵌结构体一起
s1.addr = "JJ" // 直接使用内嵌结构体的成员变量名
s1.other.addr = "AA" // 访问特定类型的内嵌结构体成员变量名,名字冲突时使用
这里顺便将内嵌与组合区分下,如果使用组合方式,则student的定义将会类似如下:
type student struct {
name string
age int
grage int
etc other // 组合other对象
}
方法
概念
在 Go 语言中,结构体与C语言的结构体等价,表示纯数据,那么Go是否支持面向对象呢?如何实现在结构体上实现方法呢?
实际上,Go的方法是作用在接收者上的一个函数,接收者是某种特定类型的变量,所以我们可以人为方法是一种特殊类型的函数,Go的这种绑定与Lisp语言非常相似。
Go中方法接收者类型几乎可以是任何类型,任何类型都可以有方法,甚至可以是函数类型。接收者不能是以下类型:
- 接口类型,接口是一个抽象定义,而方法却是具体实现,实现无法绑定在抽象定义上;
- 指针类型。
类型+方法构成了传统面向对象语言中的一个类,在 Go 中,类型的代码和绑定在它上面的方法的代码必须位于同一个包中,但可以存在于不同的源文件中(这个也就限制了您不能给int类型增加方法)。
Go语言中不支持方法重载,即对于一个类型只能有一个给定名称的方法,但是具有同样名字的方法可以在 2 个或多个不同的接收者类型上存在。
方法定义:
func (recv receiverType) methodName(paraList) (returnValueList) { ... }
范例:
func (a *denseMatrix) Add(b Matrix) Matrix
func (a *sparseMatrix) Add(b Matrix) Matrix
方法和函数的区别
上文提及到,方法是一种特殊的函数,实际上我们可以人为仅仅是格式有差异。
例如对于变量rcv:
- 使用函数时,将变量作为参数传递给函数,例如
FuncCall(recv); - 使用方法时,就像是调用变量的方法,例如
rcv.Method()。
Go的这种方法与结构独立的方式,很好的解决了耦合问题。
以指针或者值作为接收者
以值为接收者时,调用方法时会将接收者的值传递进去,所以在方法内部修改接收者时,实际上修改的时传递进来的接收者副本,并不会影响接收者本身。
以指针为接收者时,在方法内部修改接收者数据时,直接作用在接收者上。
以前面的student为例,追加2个方法:
func (s *student) funcPtr() {
s.name = "ptrName"
fmt.Printf("funcPtr: Change student name to ptrName\n")
}
func (s student) funcObj() {
s.name = "objName"
fmt.Printf("funcObj: Change student name to objName\n")
}
测试代码:
s1 := student{"wang", 10, 2, other{"JS"}}
s1.funcObj()
fmt.Println(s1)
s1.funcPtr()
fmt.Println(s1)
输出结果:
funcObj: Change student name to objName
{wang 10 2 {JS}}
funcPtr: Change student name to ptrName
{ptrName 10 2 {JS}}
结论:使用指针方式传递时,可以修改接收者的数据。
内嵌类型的方法
内嵌类型的绑定方法后,外层结构相当于自动拥有了该方法。
范例,内嵌类型Point绑定了Abs方法,此时NamedPoint类型将自动拥有该方法:
type Point struct {
x, y float64
}
func (p *Point) Abs() float64 {
return math.Sqrt(p.x*p.x + p.y*p.y)
}
type NamedPoint struct {
Point
name string
}
测试代码:
n := NamedPoint{Point{3, 4}, "Pythagoras"}
fmt.Println(n.Abs()) // 输出“5”
如果您在外层类中实现了同名方法,那么外层类型的方法将覆盖内层类型的方法。
如果一个类型包含了多个子类型,就相当于该类型继承了多个子类型的方法,等同于C++中的多重继承。
方法调用规则
Go语言约定如下方法调用规则:
- 类型 T 的可调用方法集包含接受者为 *T 或 T 的所有方法集
- 类型 *T 的可调用方法集包含接受者为 *T 的所有方法
- 类型 *T 的可调用方法集不包含接受者为 T 的方法
也就是,方法应绑定到*T的类型上。
参考
参考书籍:https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/directory.md
浙公网安备 33010602011771号