内嵌

内嵌

Go 语言中并没有子类继承这样的概念,而是通过嵌入(Embedding)的方式来实现类或者接口的组合。接口内嵌非常简单。例如:

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

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

// ReadWriter 接口结合了 Reader 和 Writer 接口。
type ReadWriter interface {
	Reader
	Writer
}

ReadWriter 能够做任何 Reader 和 Writer 可以做到的事情,它是内嵌接口的联合体(它们必须是不相交的方法集),只有接口能被嵌入到接口中。同样的基本想法可以应用在结构体中,bufio 包中有 bufio.Reader 和 bufio.Writer 这两个结构体类型,它们每一个都实现了与 io 包中相同意义的接口。此外,bufio 还通过结合 reader/writer 并将其内嵌到结构体中,实现了带缓冲的 reader/writer:它列出了结构体中的类型,但并未给予它们字段名。

// ReadWriter 存储了指向 Reader 和 Writer 的指针。
// 它实现了 io.ReadWriter。
type ReadWriter struct {
	*Reader  // *bufio.Reader
	*Writer  // *bufio.Writer
}

内嵌的元素为指向结构体的指针,当然它们在使用前必须被初始化为指向有效结构体的指针,ReadWriter 结构体和通过如下方式定义:

type ReadWriter struct {
	reader *Reader
	writer *Writer
}

内嵌类型的方法可以直接引用,这意味着 bufio.ReadWriter 不仅包括 bufio.Reader 和 bufio.Writer 的方法,它还同时满足下列三个接口:io.Reader、io.Writer 以及 io.ReadWriter。

// Server 暴露了所有 Logger 结构体的方法
type Server struct {
    Host string
	Port int

    *log.Logger
}

// 初始化方式并未受影响
server := &Server{"localhost", 80, log.New(...)}

// 却可以直接调用内嵌结构体的方法,等价于 server.Logger.Log(...)
server.Log(...)

// 内嵌结构体的名词即是类型名
var logger *log.Logger = server.Logger

案例分析

package main

import "fmt"

/*
继承
一个结构体嵌到另一个结构体,称作组合
匿名和组合的区别
如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现继承
如果一个struct嵌套了另一个【有名】的结构体,那么这个模式叫做组合
如果一个struct嵌套了多个匿名结构体,那么这个结构可以直接访问多个匿名结构体的方法,从而实现多重继承
*/

type Car struct {
    weight int
    name   string
}

func (p *Car) Run() {
    fmt.Println("running")
}

type Bike struct {
    Car
    lunzi int
}
type Train struct {
    Car
}

func (p *Train) String() string {
    str := fmt.Sprintf("name=[%s] weight=[%d]", p.name, p.weight)
    return str
}

func main() {
    var a Bike
    a.weight = 100
    a.name = "bike"
    a.lunzi = 2
    fmt.Println(a)
    a.Run()

    var b Train
    b.weight = 100
    b.name = "train"
    b.Run()
    fmt.Printf("%s", &b)
}
package main

import (
    "fmt"
    "time"
)

type Car struct {
    Name string
    Age  int
}

func (c *Car) Set(name string, age int) {
    c.Name = name
    c.Age = age
}

type Car2 struct {
    Name string
}

//Go有匿名字段特性
type Train struct {
    Car
    Car2
    createTime time.Time
    //count int   正常写法,Go的特性可以写成
    int
}

//给Train加方法,t指定接受变量的名字,变量可以叫this,t,p
func (t *Train) Set(age int) {
    t.int = age
}

func main() {
    var train Train
    train.int = 300 //这里用的匿名字段写法,给Age赋值
    //(&train).Set(1000)
    train.Car.Set("huas", 100 )
    train.Car.Name = "test" //这里Name必须得指定结构体
    fmt.Println(train)

}