2014-07-06

Go Struct

go struct

冬岛

2014-07-06

目录

  • 基本概念
  • 扩展、继承
  • Struct Field 结构
  • Struct tag
  • Struct 高级用法

基本概念

定义一个 struct

 type Circle struct {
     x float64
     y float64
     r float64
 }

 type Circle struct {
     x, y, r float64
 }

初始化

 type Circle struct {
     x float64
     y float64
     r float64
 }

 var c Circle
 c := Circle{x: 0, y: 0, r: 5}
 c := Circle{0, 0, 5}
 c := new(Circle)
 c := &Circle{0, 0, 5}

method 初始化

 package main
 
 import "fmt"
 
 type Rectangle struct {
     length, width int
 }
 
 func (r Rectangle) Area() int {
     return r.length * r.width
 }
 func main() {
     r1 := Rectangle{4, 3}
     fmt.Println("Rectangle is: ", r1)
     fmt.Println("Rectangle area is: ", r1.Area())
 }
 // Rectangle is: {4 3}
 // Rectangle area is: 12

method 初始化

  • 指针和非指针两种方式
 package main
 
 import "fmt"
 
 type Rectangle struct {
     length, width int
 }
 
 func (r Rectangle) Area_by_value() int {
     return r.length * r.width
 }
 
 func (r *Rectangle) Area_by_reference() int {
     return r.length * r.width
 }
 
 func main() {
     r1 := Rectangle{4, 3}
     fmt.Println("Rectangle is: ", r1)
     fmt.Println("Rectangle area is: ", r1.Area_by_value())
     fmt.Println("Rectangle area is: ", r1.Area_by_reference())
     fmt.Println("Rectangle area is: ", (&r1).Area_by_value())
     fmt.Println("Rectangle area is: ", (&r1).Area_by_reference())
 }

 // Rectangle is: {4 3}
 // Rectangle area is: 12
 // Rectangle area is: 12
 // Rectangle area is: 12
 // Rectangle area is: 12

Note: 这么容易就可以给 struct 添加成员函数, 是不是以为着咱们可以他 time package 添加一个函数呢

指针和非指针方式真的一样吗?

 package main
 
 import "fmt"
 
 type Rectangle struct {
     length, width int
     val, val_by_ref int
 }
 
 func (r Rectangle) String() string{
     str := fmt.Sprintf("length: %d, whdth: %d, val: %d, val_by_ref: %d", r.length, r.width, r.val, r.val_by_ref)
     return str
 }
 func (r Rectangle) Area_by_value() {
     r.val = r.length * r.width
 }
 
 func (r *Rectangle) Area_by_reference() {
     r.val_by_ref = r.length * r.width
 }
 
 func main() {
     r1 := Rectangle{length:4, width:3}
     fmt.Println("Rectangle is: ", r1)
 
     r1.Area_by_value()
     r1.Area_by_reference()
 
     fmt.Println("Rectangle area is: ", r1.val)
     fmt.Println("Rectangle area is: ", r1.val_by_ref)
 }

 // Rectangle is:  length: 4, whdth: 3, val: 0, val_by_ref: 0
 // Rectangle area is:  0
 // Rectangle area is:  12

why?

 package main
 
 import "fmt"
 
 type Rectangle struct {
     length, width int
     val, val_by_ref int
 }
 
 func (r Rectangle) String() string{
     str := fmt.Sprintf("length: %d, whdth: %d, val: %d, val_by_ref: %d", r.length, r.width, r.val, r.val_by_ref)
     return str
 }
 func (r Rectangle) ref() {
     fmt.Printf("obj ref ref is: %p\n", &r)
 }
 
 func (r *Rectangle) ref_by_reference() {
     fmt.Printf("obj ref ref_by_reference is: %p\n", r)
 }
 
 func main() {
     r1 := new(Rectangle)
     fmt.Printf("obj ref is: %p\n", r1)
     r1.ref()
     r1.ref_by_reference()
 
 }
 // obj ref is: 0xc210049000
 // obj ref ref is: 0xc210049040
 // obj ref ref_by_reference is: 0xc210049000
  • 非指针时复制了一个 struct 对象
  • 是指针时指向的是同一个 struct 对象

给 time package 添加一个成员函数

  • 现在我们已经知道如何添加成员函数了, 现在我们为 time package 添加一个成员函数
 func (t time.Time) first5Chars() string {
     return time.LocalTime().String()[0:5]
 }
 // cannot define new methods on non-local type time.Time
  • 因为不是在同一个 package 文件中声明的, 所以不能添加成员函数
  • 那么我们是否有办法扩展 time package 的功能呢?

扩展、继承

 package main
 
 import "fmt"
 import "time"
 
 type myTime struct {
     time.Time //anonymous field
 }
 
 func (t myTime) first5Chars() string {
     return t.Time.String()[0:5]
 }
 
 func main() {
     m := myTime{*time.LocalTime()} //since time.LocalTime returns an address, we convert it to a value with *
     fmt.Println("Full time now:", m.String()) //calling existing String method on anonymous Time field
     fmt.Println("First 5 chars:", m.first5Chars()) //calling myTime.first5Chars
 }
 // Full time now: Tue Nov 10 23:00:00 UTC 2009
 // First 5 chars: Tue N

扩展、继承

  • beengo 也是采用同样的方式实现”继承”的
 // github.com/astaxie/beego/config/json.go
 43 // A Config represents the json configuration.
 44 // Only when get value, support key as section:name type.
 45 type JsonConfigContainer struct {
 46 	data map[string]interface{}
 47 	sync.RWMutex
 48 }

 125 // WriteValue writes a new value for key.
 126 func (c *JsonConfigContainer) Set(key, val string) error {
 127 	c.Lock()
 128 	defer c.Unlock()
 129 	c.data[key] = val
 130 	return nil
 131 }

扩展、继承

 package main
 
 import (
     "fmt"
 )
 
 type Mammal struct {
     Lung string
 }
 
 type Fish struct {
     Gills string
 }
 
 type Amphibian struct {
     Mammal
     Fish
     Name string
 }
 
 func main() {
     salamander := new(Amphibian)
     salamander.Name = "salamander"
     fmt.Print(salamander.Name + " ")
 
     salamander.Lung = "Lung"
     fmt.Print("has " + salamander.Lung)
 
     salamander.Gills = "Gills"
     fmt.Println(" and " + salamander.Gills)
 }

 // salamander has Lung and Gills

多态性

  • 正确的使用方法
 package main
 
 import (
     "fmt"
 )
 
 type Mammal struct {
     Lung string
 }
 
 type Fish struct {
     Gills string
 }
 
 type Amphibian struct {
     Mammal
     Fish
     Name string
 }
 
 func main() {
     salamandar := Amphibian{}
     salamandar.Name = "Salamandar"
     fmt.Print(salamandar.Name + " ")
 
     salamandar.Lung = "Lung"
     fmt.Print("has " + salamandar.Lung)
 
     salamandar.Gills = "Gills"
     fmt.Println(" and " + salamandar.Gills)
 
     lion := salamandar.Mammal
     fmt.Println("Lion has a " + lion.Lung)
 }

 // Salamandar has Lung and Gills
 // Lion has a Lung

基成员冲突

 package main
 
 import (
     "fmt"
 )
 
 type Mammal struct {
     Lung string
     Lips string
 }
 
 type Fish struct {
     Gills string
     Lips string
 }
 
 type Amphibian struct {
     Mammal
     Fish
     Name string
 }
 
 func main() {
     salamander := new(Amphibian)
     salamander.Name = "salamander"
     fmt.Print(salamander.Name + " ")
 
     salamander.Lung = "Lung"
     fmt.Print("has " + salamander.Lung)
 
     salamander.Gills = "Gills"
     fmt.Println(" and " + salamander.Gills)
 
     salamander.Lips = "lips"
     mt.Println(salamander.Name + " has " + salamander.Lips)
 }
 // ./mulity-extend.go:34: ambiguous selector salamander.Lips
  • 多重扩展的时候如果不同的基类有相同的 field , 那么在子 struct 中直接引用就会报错, 因为子 struct 不知道你想引用的是哪个基类的 field

基成员冲突

 package main
 
 import (
     "fmt"
 )
 
 type Mammal struct {
     Lung string
     Lips string
 }
 
 type Fish struct {
     Gills string
     Lips string
 }
 
 type Amphibian struct {
     Mammal
     Fish
     Name string
 }
 
 func main() {
     salamander := new(Amphibian)
     salamander.Name = "salamander"
     fmt.Print(salamander.Name + " ")
 
     salamander.Lung = "Lung"
     fmt.Print("has " + salamander.Lung)
 
     salamander.Gills = "Gills"
     fmt.Println(" and " + salamander.Gills)
 
     salamander.Mammal.Lips = "Mammal lips"
     salamander.Fish.Lips = "Fish lips"
     fmt.Println(salamander.Name + " has both " + salamander.Mammal.Lips + " and " + salamander.Fish.Lips)
 }
 
 // salamander has Lung and Gills
 // salamander has both Mammal lips and Fish lips
  • 不同基类相同 filed 名称的时候可以通过”全路径名”的方式引

基于指针实现的扩展

 package main
 
 import (
     "fmt"
 )
 
 type Mammal struct {
     Lung string
 }
 
 type Fish struct {
     Gills string
 }
 
 type Amphibian struct {
     *Mammal
     *Fish
     Name string
 }
 
 func main() {
     salamander := new(Amphibian)
     salamander.Name = "salamander"
     fmt.Print(salamander.Name + " ")
 
     salamander.Lung = "Lung"
     fmt.Print("has " + salamander.Lung)
 
     salamander.Gills = "Gills"
     fmt.Println("and " + salamander.Gills)
 }

 // salamander panic: runtime error: invalid memory address or nil pointer dereference
 // [signal 0xb code=0x1 addr=0x0 pc=0x400f70]
 // 
 // goroutine 1 [running]:
 // runtime.panic(0x499d60, 0x565b28)
 //     /usr/lib/golang/src/pkg/runtime/panic.c:266 +0xb6
 // main.main()
 //     /root/tmp/go/slide/mulity-extend-point.go:26 +0x370
 // exit status 2
  • 基于指针的扩展需要手动初始化 base struct

基于指针实现的扩展

 package main
 
 import (
     "fmt"
 )
 
 type Mammal struct {
     Lung string
 }
 
 type Fish struct {
     Gills string
 }
 
 type Amphibian struct {
     *Mammal
     *Fish
     Name string
 }
 
 func NewAmphibian() *Amphibian {
     ret := new(Amphibian)
     ret.Mammal = new(Mammal)
     ret.Fish = new(Fish)
     return ret
 }
 
 func main() {
     salamander := NewAmphibian()
     salamander.Name = "salamander"
     fmt.Print(salamander.Name + " ")
 
     salamander.Lung = "Lung"
     fmt.Print("has " + salamander.Lung)
 
     salamander.Gills = "Gills"
     fmt.Println(" and " + salamander.Gills)
 }

 // salamander has Lung and Gills

深刻理解继承

 package main
 
 import "fmt"
 
 type Dog struct {
     name string
 }
 type BDog struct {
     Dog
     name string
 }
 type Funny interface {
     callMyName()
     getName() string
 }
 func (this *Dog) callMyName() {
     fmt.Printf("my name is %q\n", this.name)
 }
 func main() {
     b := new(BDog)
     b.name = "BDog"
     b.Dog.name = "Dog"
     b.callMyName()
 }
  • 输出结果: my name is “Dog”

深刻理解继承

 package main
 
 import "fmt"
 
 type Dog struct {
     name string
 }
 type BDog struct {
     Dog
     name string
 }
 type Funny interface {
     callMyName()
     getName() string
 }
 func (this *Dog) callMyName() {
     fmt.Printf("my name is %q\n", this.name)
 }
 
 func (this *BDog) callMyName() {
     fmt.Printf("my name is %q\n", this.name)
 }
 
 func main() {
     b := new(BDog)
     b.name = "BDog"
     b.Dog.name = "Dog"
     b.callMyName()
 }
  • 输出结果: my name is “BDog”

struct field

  • struct field 的定义
type StructField struct {
    // Name is the field name.
    // PkgPath is the package path that qualifies a lower case (unexported)
    // field name.  It is empty for upper case (exported) field names.
    // See http://golang.org/ref/spec#Uniqueness_of_identifiers
    Name    string
    PkgPath string

    Type      Type      // field type
    Tag       StructTag // field tag string
    Offset    uintptr   // offset within struct, in bytes
    Index     []int     // index sequence for Type.FieldByIndex
    Anonymous bool      // is an embedded field
}

struct field

  • struct field 实例
 package main
 
 import (
     "fmt"
     "reflect"
 )
 
 type T struct {
     A int     `json:key-a`
     B string  `json:key-b`
 }
 
 func main() {
     t := T{23, "skidoo"}
     st := reflect.TypeOf(t)
     for i := 0; i < 2; i++ {
         f := st.Field(i)
         fmt.Printf("index: %d, name: %s, type: %s, pkgPath: %s, Anonymous: %t, tag: %s\n", i,
         f.Name, f.Type, f.PkgPath, f.Anonymous ,f.Tag)
     }
 }
 // index: 0, name: A, type: int,    pkgPath: , Anonymous: false, tag: json:key-a
 // index: 1, name: B, type: string, pkgPath: , Anonymous: false, tag: json:key-b

struct tag

 package main
 
 import (
     "fmt"
     "reflect"
 )
 
 func main() {
     type S struct {
         F string `species:"gopher" color:"blue"`
     }
 
     s := S{}
     st := reflect.TypeOf(s)
     field := st.Field(0)
     fmt.Println(field.Tag.Get("color"), field.Tag.Get("species"))
 
 }

 // blue gopher

struct tag 使用场景

  • 序列化
  • 反序列化

json 序列化

 import "encoding/json"

 type Result struct {
     DomainGroupId string  `json:"domainGroupId"`
     StrategyList []string `json:"strategyList"`
 }
 

Struct 高级用法

一组变量

 var config struct {
     APIKey      string
     OAuthConfig oauth.Config
 }
 
 config.APIKey = "BADC0C0A"

快速创建/模板数据

 package main
 
 import "fmt"
 
 
 func main() {
     data := struct {
         name string
         age int
     }{
        "jack",
        20,
     }

     fmt.Println("data: ", data)
 }

 // data:  {jack 20}

Test tables

 package main
 
 import "fmt"
 
 
 func main() {
     var indexRuneTests = []struct {
         s    string
         out  int
     }{
         {"a A x", 2},
         {"some_text=some_value", 9},
         {"☺a", 3},
         {"a☻☺b", 4},
     }
 
     fmt.Println("data: ", indexRuneTests)
 }
 // data:  [{a A x 2} {some_text=some_value 9} {☺a 3} {a☻☺b 4}]

Nested structs

 {
    "data": {
        "children": [{
            "data": {
                "title": "The Go homepage",
                "url": "http://golang.org/"
            }
        }]
    }
}
 type Item struct {
     Title string
     URL   string
 }
 
 type Response struct {
     Data struct {
         Children []struct {
             Data Item
         }
     }
 }

参考文献