Go技术文档go学习

golang 结构体和方法

2018-06-30  本文已影响98人  百味纯净水

1. 定义

结构体是将另个或者多个任意类型的命名变量组合在一起的聚合数据类型。

2. 成员变量

type A struct {
    Hour int    //可导出
    minute int  //不可导出
}
type B struct {
    value int
    //Next B   //错误
    Next *B    //正确
}

3. 结构体比较

  1. 如果结构体的所有成员变量都可以比较,那么这个结构体是可以比较的。两个结构体的比较可以使用==或者!=。
type C struct {
    A int
    B string
}
c1 := C{A:1, B:"abc"}
c2 := C{A:1, B:"abc"}
c3 := C{A:2, B:"abc"}
fmt.Println(c1.A == c2.A && c1.B == c2.B) //true
fmt.Println(c1 == c2)                        //true 与上等价
fmt.Println(c1.A == c3.A && c1.B == c3.B) //false
fmt.Println(c1 == c3)                        //false 与上等价

2.和其他可比较的类型一样,可比较的结构体类型都可以作为map的键类型。

type C struct {
    A int
    B string
}
mp := make(map[C]int)
key := C{A:1, B:"abc"}
mp[key] = 9
fmt.Println(mp[C{A:1, B:"abc"}])  //9

4. 结构体嵌套和匿名成员

  1. go允许我们定义不带名称的结构体成员,只需要指定类型即可;这种结构体成员称作匿名成员。这个结构体成员的类型必须是一个命名类型或者指向命名类型的指针。正是因为有了这种结构体嵌套的功能,我们才能直接访问到我们需要的变量而不是指定一大串中间变量。
type Point struct {
    X int
    Y int
}
type Circle struct {
    Point
}
var c Circle
c.X = 10  //等价于 c.Point.X = 10
c.Y = 10  //等价于 c.Point.Y = 10

type Wheel struct {
    *Point
}
  1. 结构体字面量初始化没有快捷方式,必须遵循形状类型的定义。
type Point struct {
    X int
    Y int
}
type Circle struct {
    Point
}
var c Circle
c = Circle{1,1}  //错误
c = Circle{Point{1,1}}  //正确
c = Circle{Point: Point{1,1}}  //正确

3.因为“匿名成员”拥有隐式的名字,所以你不能在一个结构体里面定义两个相同类型的匿名成员,否则会引起冲突。由于匿名成员的名字是由它们的类型决定的,因此它们的可导出性也是由他们的的类型决定。在下面的例子中,point和circle这两个匿名成员是可导出的,即使这两个结构体是不可导出的(point和circle)。

type point struct {
    X int
    Y int
}
type circle struct {
    point
}

type Wheel struct {
    circle
}
var w Wheel
w.X = 8 // 等价于 w.circle.point.X = 8, w.X是可导出的,w.circle.point.X是不可导出的

5. 结构体方法

  1. 定义
    方法的声明和普通函数的声明类似,知识在函数名字前面多加了一个参数。这个参数把这个方法绑定到这个参数对应的类型上。
    附加的参数p成为方法的接收者, 它源于早先的面向对象语言,用来描述主调方法向对象发送消息。
type Point struct {
    X int
    Y int
}
func (p Point) print() {
    fmt.Println(p.X, p.Y)
}
  1. 方法定义限制
    go和许多其他面向对象的语言不同,它可以将方法绑定到除了指针类型和接口类型的任何类型上。可以很方便的为简单的类型(入整形、字符串、slice、map、甚至函数等)定义附加的行为。
//整形
type III int
func (i III) print() {
    fmt.Println(i)
}

//函数
func Test() {
    fmt.Println("test")
}
type FunT func()
func (f FunT) Print() {
    fmt.Println(f)
}
func main() {
    var f FunT
    f = Test
    f.Print()
}
  1. 指针接收者的方法
    由于主调函数会复制每一个实参变量,或者如果一个实参太大而我们希望避免复制整个实参,因此我们必须使用指针来传递变量的地址。这也同样适用于更新接收者我们将它绑定到指针类型。在调用方法的时候,编译器会对变量进行隐式转换。
    总结一下结构体方法可以成功调用的条件:
type Point struct {
    X int
    Y int
}
func (p Point) Print() {
    fmt.Println(p.X, p.Y)
}
func (p *Point) ScaleBy(factor int) {
    p.X *= factor
    p.Y *= factor
}
func main() {
    p := Point{1,1}
    ptr := &p
    p.Print()   //1. 正确
    ptr.Print() //2. 正确
    p.ScaleBy(2)      //3. 正确
    ptr.ScaleBy(2)    //4. 正确
    Point{1,1}.Print()    //5. 正确
    (&Point{1,1}).Print() //6. 正确
    (&Point{1,1}).ScaleBy( 2) //7. 正确
    Point{1,1}.ScaleBy( 2)    //8. 错误
}
type A struct {
    Data int
}
func (a *A) FunPtrA() {
    fmt.Println("FunPtrA")
}
func main() {
    var ptrA *A
    ptrA = nil
    ptrA.FunPtrA()  //FunPtrA
}

4. 通过结构体内嵌组成类型
这里主要讨论内嵌结构体和结构体指针后,对于源结构体定义函数的访问权限。

type A struct {
    Data int
}
func (a A) FunA() {
    fmt.Println(a.Data)
}
func (a *A) FunPtrA1() {
    fmt.Println("FunPtrA1")
}
func (a *A) FunPtrA2() {
    fmt.Println("FunPtrA2")
    fmt.Println(a.Data)
}

type B struct {
    A
}
type C struct {
    *A
}
func main() {
    var b B
    b.FunA()    //可以访问
    b.FunPtrA1() //可以访问
    b.FunPtrA2() //可以访问

    var c C
    //c.FunA()     //不可访问
    c.FunPtrA1() //可以访问
    //c.FunPtrA2() //不可访问

    c.A = &A{}
    c.FunA()     //可以访问
    c.FunPtrA1() //可以访问
    c.FunPtrA2() //可以访问
}

对于以上内容有疑问,欢迎留言讨论或者私信。转载请注明出处

上一篇下一篇

猜你喜欢

热点阅读