普通结构体
1.public
type Flower struct { Name string Color string Category string}
外部包可以直接引用和赋值
2.private
type leaf struct { count int size int}
外部包不能直接引用
需要定义public函数提供操作,例如
func (l *leaf) SetColor(color string) { l.color = color}
3.public结构体里面有private
type Flower struct { Name string Color string Category string}
这种情况下,只能通过public的函数或者方法来操作内部的private属性
4.private结构体里面有public属性
type leaf struct { color string size int Count int}
这种也是允许的,例如:
func NewLeaf(color string, size, count int) *leaf{ var l = leaf{color, size, count} return &l}
在另一个包中引用这些结构体
func main() { leaf := plant.NewLeaf("green", 5, 10) fmt.Println(leaf) leaf.Count = 5 fmt.Println(leaf)}
输出:
&{green 5 10}&{green 5 5}
很少看到这么使用
嵌套结构体
type Leaf struct { Color string Size int Count int}type Flower struct { Leaf Leaf Name string Color string Category string}
访问方式:
func main() { f := plant.Flower{} f.Category = "Roseceae" f.Name = "Rose" f.Color = "Red" f.Leaf.Color = "green" f.Leaf.Count = 5 f.Leaf.Size = 10 fmt.Println(f)}
这种嵌套,在访问内部结构的成员时,必须显示的指向,如:
f.Leaf.Color
嵌套匿名结构体
type Leaf struct { Color string Size int Count int}type Flower struct { Leaf Name string Color string Category string}
访问方式:
func main() { f := plant.Flower{} f.Category = "Roseceae" f.Name = "Rose" f.Color = "Red" f.Count = 5 f.Size = 10 f.Leaf.Color = "green" fmt.Println(f)}
如上可以看出,有两种访问方式:
a.直接由外部结构体访问内部结构体成员,跟访问外部结构体内部自己的成员一样
f.Count = 5f.Size = 10
b.外部结构体指向内部结构体,再访问其内部成员
f.Leaf.Color = "green"
注意:在内外结构体有成员名称相同时,如果访问嵌套结构体的成员,必须按照b方式访问,否则是直接访问的外层结构体成员变量。
结构体中嵌套接口
以golang内置的包context为例
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{}}// A canceler is a context type that can be canceled directly. The// implementations are *cancelCtx and *timerCtx.type canceler interface { cancel(removeFromParent bool, err error) Done() <-chan struct{}}// A cancelCtx can be canceled. When canceled, it also cancels any children// that implement canceler.type cancelCtx struct { Context mu sync.Mutex // protects following fields done chan struct{} // created lazily, closed by first cancel call children map[canceler]struct{} // set to nil by the first cancel call err error // set to non-nil by the first cancel call}
这种嵌套,可以当成继承来理解,
首先Context接口有四个方法,全部被cancelCtx这个结构体继承了,然后重写了其中三个方法
func (c *cancelCtx) Value(key interface{}) interface{} { if key == &cancelCtxKey { return c } return c.Context.Value(key)}func (c *cancelCtx) Done() <-chan struct{} { c.mu.Lock() if c.done == nil { c.done = make(chan struct{}) } d := c.done c.mu.Unlock() return d}func (c *cancelCtx) Err() error { c.mu.Lock() err := c.err c.mu.Unlock() return err}
同时,cancelCtx又实现了canceler这个接口定义的方法,所以cancelCtx又可以当成canceler接口用:
// cancel closes c.done, cancels each of c's children, and, if// removeFromParent is true, removes c from its parent's children.func (c *cancelCtx) cancel(removeFromParent bool, err error) { //具体实现见源码}func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { c := newCancelCtx(parent) propagateCancel(parent, &c) return &c, func() { c.cancel(true, Canceled) }}// newCancelCtx returns an initialized cancelCtx.func newCancelCtx(parent Context) cancelCtx { return cancelCtx{Context: parent}}// propagateCancel arranges for child to be canceled when parent is.func propagateCancel(parent Context, child canceler) { //具体实现见源码}
从WithCancel中可以看到,执行c := newCancelCtx(parent)之后,c其实是cancelCtx类型,再调用propagateCancel时,第二个参数是canceler接口类型,直接取c地址传入,然后&c就当canceler接口用了,最后return时,又将&c当Context接口返回了
Context包代码不多,可以自己研究源码,认识会更深刻。
json/xml结构体
JSON:
Json结构体定义的时候,成员名称和结构体名称必须可导出,即必须以大写开头,如:
type Os struct { Arch string Major string Minor string Name string Platform string Version string}
但是,这样在序列化成json字符串的时候,会以成员名称作为json字符串中的名称:
func main() { linux := Os{ Arch: "x86_64", Major: "7", Minor: "7", Name: "CentOS Linux", Version: "7.7", } js, err := json.Marshal(linux) if err != nil { return } fmt.Println(string(js))}
输出:
{"Arch":"x86_64","Major":"7","Minor":"7","Name":"CentOS Linux","Platform":"","Version":"7.7"}
有些情况下,我们的json字符串想全部以小写开头,则可以用如下方法指定输出时的字符串名称:
type Os struct { Arch string `json:"arch"` Major string `json:"major"` Minor string `json:"minor"` Name string `json:"name"` Platform string `json:"platform"` Version string `json:"version"`}
可以直接用内置的接口进行序列化处理:
func main() { linux := Os{ Arch: "x86_64", Major: "7", Minor: "7", Name: "CentOS Linux", Platform: "centos", Version: "7.7", } js, err := json.Marshal(linux) if err != nil { return } fmt.Println(string(js))}
输出:
{"arch":"x86_64","major":"7","minor":"7","name":"CentOS Linux","platform":"centos","version":"7.7"}
如果某个字段没有赋值,例如Platform:
func main() { linux := Os{ Arch: "x86_64", Major: "7", Minor: "7", Name: "CentOS Linux", Version: "7.7", } js, err := json.Marshal(linux) if err != nil { return } fmt.Println(string(js))}
输出:
{"arch":"x86_64","major":"7","minor":"7","name":"CentOS Linux","platform":"","version":"7.7"}
可以看到json字符串中仍然生成了platform这个字段,如果想对于某些不赋值的字段在生成json串的时候忽略掉,则可以在定义结构体的时候,使用omitempty,定义如下:
type Os struct { Arch string `json:"arch"` Major string `json:"major"` Minor string `json:"minor"` Name string `json:"name"` Platform string `json:"platform,omitempty"` Version string `json:"version"`}func main() { linux := Os{ Arch: "x86_64", Major: "7", Minor: "7", Name: "CentOS Linux", Version: "7.7", } js, err := json.Marshal(linux) if err != nil { return } fmt.Println(string(js))}
输出:
{"arch":"x86_64","major":"7","minor":"7","name":"CentOS Linux","version":"7.7"}
注意注意注意:
`json:"platform,omitempty"`
这个字符串中不能包含空格,否则无效
例如结构体中定义为:
Platform string `json: "platform,omitempty"`
这种json:后面跟空格,则不能指定json序列化时候显示的成员名称,默认是结构体成员名,即Platform
{"arch":"x86_64","major":"7","minor":"7","name":"CentOS Linux","Platform":"","version":"7.7"}
文章来源:智云一二三科技
文章标题:golang 结构体
文章地址:https://www.zhihuclub.com/5932.shtml