golang没有继承的机制,但有嵌入的机制,将其他interface和struct嵌入当前类型,以起到复用的作用,达到继承的效果。

interface嵌入interface,如下常见的例子

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface {
    Reader
    Writer
}

struct也可以嵌入struct,使用了匿名字段机制,只嵌入stuct类型名称,而没有字段名称。

// ReadWriter stores pointers to a Reader and a Writer.
// It implements io.ReadWriter.
type ReadWriter struct {
    *Reader  // *bufio.Reader
    *Writer  // *bufio.Writer
}

struct ReadWriter可以直接用.引用嵌入类型的字段和方法,不用写上类型名称。当然,在引用字段和类型时,一定要对这个类型进行实例初始化。

以下列出了嵌入式类型使用的注意事项

外层类型调用方法

外层类型调用方法会传到到内嵌的类型

package main

import (
	"fmt"
)

type Dog struct {
	sound string
}

func (d *Dog) Sound() string {
	return d.sound
}

type Animal struct {
	Dog
	sound string
}

func main() {
	a := Animal{
		sound: "...",
		Dog:   Dog{sound: "woof"},
	}
	fmt.Println(a.Sound()) // <- woof
}

上面的例子,调用Animal的Sound方法,最终调用的时Dog的Sound方法。

字段名称冲突

外层类型的字段或方法会覆盖内部类型的字段和方法

package main

import (
	"fmt"
)

type C struct {
	Hello string
}

type B struct {
	C
}

type A struct {
	B
        Hello string // <- added
}

func main() {
	A := A{Hello: "world", B: B{C: C{Hello: "universe"}}}
	fmt.Println(A.Hello) // <- world
}

A的Hello字段覆盖了C的Hello。

对于同级的字段名称冲突,可以直接用A.B.Hello明确访问那个类型的字段,而直接用A.Hello访问就会编译错误。

package main

import (
	"fmt"
)

type B struct {
	Hello string
}

type C struct {
	Hello string
}

type A struct {
	B
	C
}

func main() {
	A := A{B: B{Hello: "world 1"}, C: C{Hello: "world 2"}}
	fmt.Println(A.Hello) // <- not allowed, will break
	fmt.Println(A.B.Hello) // <- world 1
	fmt.Println(A.C.Hello) // <- world 2
}

Marshalling / Unmarshalling

嵌入式类型,将json字符串Unmarshall成struct会有意向不到的错误,特别是遇到同级字段名称冲突的情况。如下面的例子,Color就不会展示出来。代码本身时可以编译成功的。

dogpackage main

import (
	"encoding/json"
	"fmt"
)

type Dog struct {
	Color string `json:"color"`
}

type Cat struct {
	Color string `json:"color"`
}

type Animal struct {
	Dog
	Cat
	Kind string `json:"kind"`
}

func main() {
	var a Animal
	err := json.Unmarshal([]byte(`{"kind":"dog","color":"golden"}`), &a)
	if err != nil {
		panic(err)
	}

	fmt.Println(a.Kind)       //   dog  
	fmt.Println(a.Dog.Color)  // not print
	fmt.Println(a.Cat.Color)  // not print
}

json的marshall和unmarshall也是依赖golang对字段的可见原则,同时也考虑了json的tag机制。会找到最匹配的字段进行编码。如果同层次的字段名称,tag都一样,会忽略编码,也不会报错。

要解决这个问题,可以重写类型的UnmarshallJson方法

package main

import (
	"encoding/json"
	"fmt"
)

type Dog struct {
	Color string `json:"color"`
}

type Cat struct {
	Color string `json:"color"`
}

type AnimalBase struct {
	Kind string `json:"kind"`
}

type Animal struct {
	AnimalBase
	Dog
	Cat
}

func (a *Animal) UnmarshalJSON(raw []byte) error {
	var base AnimalBase
	err := json.Unmarshal(raw, &base)
	if err != nil {
		return err
	}

	a.AnimalBase = base

	switch base.Kind {
	case "dog":
		var dog Dog
		err = json.Unmarshal(raw, &dog)
		if err != nil {
			return err
		}
		a.Dog = dog
	case "cat":
		var cat Cat
		err = json.Unmarshal(raw, &cat)
		if err != nil {
			return err
		}
		a.Cat = cat
	}
	return nil

}

func main() {
	var a Animal
	err := json.Unmarshal([]byte(`{"kind": "dog", "color":"golden"}`), &a)
	if err != nil {
		panic(err)
	}

	fmt.Println(a.Kind)
	fmt.Println(a.Dog.Color)
	fmt.Println(a.Cat.Color)
}

以下内容为interface in struct的一些注意点。文章内容来自Embedding in Go: Part 3 - interfaces in structs

简单的例子

type Fooer interface {
  Foo() string
}

type Container struct {
  Fooer
}

func (cont Container) Foo() string {
  return cont.Fooer.Foo()
}

// sink takes a value implementing the Fooer interface.
func sink(f Fooer) {
  fmt.Println("sink:", f.Foo())
}

// TheRealFoo is a type that implements the Fooer interface.
type TheRealFoo struct {
}

func (trf TheRealFoo) Foo() string {
  return "TheRealFoo Foo"
}

co := Container{Fooer: TheRealFoo{}}
sink(co)

如果Container初始化为co := Container() 则执行sink的时候会报错。

interface in struct使用模式的举例

interface包裹

实现StatsConn struct, 对读取的数据进行字节统计

type StatsConn struct {
  net.Conn

  BytesRead uint64
}

func (sc *StatsConn) Read(p []byte) (int, error) {
  n, err := sc.Conn.Read(p)
  sc.BytesRead += uint64(n)
  return n, err
}

conn, err := net.Dial("tcp", u.Host+":80")
if err != nil {
  log.Fatal(err)
}
sconn := &StatsConn{conn, 0}

resp, err := ioutil.ReadAll(sconn)
if err != nil {
  log.Fatal(err)
}

对类型进行了扩充。

当然也可用用其他方式去实现这个功能,但需要重写接口的所有方法,比较繁琐。如下的普通实现举例

type StatsConn struct {
  conn net.Conn

  BytesRead uint64
}

func (sc *StatsConn) Read(p []byte) (int, error) {
  n, err := sc.conn.Read(p)
  sc.BytesRead += uint64(n)
  return n, err
}

//there are 8 methods that should be rewrite
func (sc *StatsConn) Close() error {
  return sc.conn.Close()
}


func (sc *StatsConn) Write() error {
  return sc.conn.Write()
}

例子2: Sort.Reverse

继承了Sort.Interface,修改了Swap的方法,方便实现逆序排序。

type Interface interface {
    // Len is the number of elements in the collection.
    Len() int
    // Less reports whether the element with
    // index i should sort before the element with index j.
    Less(i, j int) bool
    // Swap swaps the elements with indexes i and j.
    Swap(i, j int)
}

type reverse struct {
  sort.Interface
}

func (r reverse) Less(i, j int) bool {
  return r.Interface.Less(j, i)
}

func Reverse(data sort.Interface) sort.Interface {
  return reverse{data}
}

sort.Sort(sort.Reverse(sort.IntSlice(lst)))
fmt.Println(lst)

例子3: context.WithValue

valueCtx扩充了Context

func WithValue(parent Context, key, val interface{}) Context {
  return &valueCtx{parent, key, val}
}

type valueCtx struct {
  Context
  key, val interface{}
}

func (c *valueCtx) Value(key interface{}) interface{} {
  if c.key == key {
    return c.val
  }
  return c.Context.Value(key)
}


例子4:将一个类型降低接口实现级别

这个是一个高级的功能,降低了一个类型的接口实现级别,为了解决嵌入循环依赖的问题。

首先看下io.ReaderFrom接口定义

type ReaderFrom interface {
    ReadFrom(r Reader) (n int64, err error)
}

os.File类型实现了ReaderFrom这个接口

func (f *File) ReadFrom(r io.Reader) (n int64, err error) {
  if err := f.checkValid("write"); err != nil {
    return 0, err
  }
  n, handled, e := f.readFrom(r)
  if !handled {
    return genericReadFrom(f, r)
  }
  return n, f.wrapErr("write", e)
}

其中关键是调用了genericReadFrom,在这个函数中会调用io.Copy函数

func genericReadFrom(f *File, r io.Reader) (int64, error) {
  return io.Copy(onlyWriter{f}, r)
}

onlyWriter是怎么定义的呢?onlyWriter是一个struct,嵌入了io.Writer interface

type onlyWriter struct {
  io.Writer
}

奇怪的是代码中并没有看到onlyWriter的定义。那这个有什么用处呢。这个要看看io.Copy函数中的逻辑了,io.Copy函数是调用f.ReadFrom,同时我们就是为了实现ReadFrom这个method,会形成循环依赖。所以要单独定义onlyWriter打破这个依赖。