Go 编程基础 interface 11

接口interface

  • 接口是一个或多个方法签名的集合
  • 只要某个类型拥有该接口的所有方法签名, 即算实现该接口, 无需显示声明实现了哪个接口, 这称为Structural Typing
  • 接口只有方法申明,没有实现,没有数据字段
  • 接口可以匿名嵌入其他接口, 或嵌入的结构中
  • 将对象赋值给接口时, 会发生拷贝, 而接口内部储存的是指向这个复制品的指针, 既无法修改复制品的状态, 也无法获取指针
  • 只有当接口存储的类型和对象都为nil时 接口才等于nil
  • 接口调用不会做receiver的自动转换
  • 接口同样支持匿名字段方法
  • 接口也可实现类型OOP中的多态
  • 空接口可以作为任何数据类型的容器

定义接口和实现

  • 定义一个USB的接口 有一个活的当前连接设备名称的方法 和 连接方法
  • 定义一个结构体去实现这个USB接口
type USB interface {
    //定义一个Name方法, 返回值是string
    Name() string
    //定义连接方法,无返回值
    Connect()
}

//手机接口
type PcConnecter struct {
    ClientName string
}

func (pc PcConnecter) Name() string {
    return pc.ClientName
}

func (pc PcConnecter) Connect() {
    fmt.Println("connected: ", pc.ClientName)
}

//关闭连接
func Disconnect(use USB) {
    fmt.Println("Disconnect")
}

func main() {
    var a USB
    a = PcConnecter{ClientName: "pc"}
    a.Connect()
    fmt.Println(a.Name())
    Disconnect(a)
}
connected:  pc
pc
Disconnect

嵌入接口

在定义复杂接口的时候会用到

我们在上面的例子中.是把Name方法和Connect方法放在了一个接口,那么我们现在就吧Connect单独写成一个接口嵌入到USB接口中

type USB interface {
    //定义一个Name方法, 返回值是string
    Name() string
    Connecter
}
//单独定义连接接口
type Connecter interface {
    Connect()
}

//手机接口
type PcConnecter struct {
    ClientName string
}

func (pc PcConnecter) Name() string {
    return pc.ClientName
}

func (pc PcConnecter) Connect() {
    fmt.Println("connected: ", pc.ClientName)
}

//关闭连接
func Disconnect(use USB) {
    fmt.Println("Disconnect")
}

func main() {
    var a USB
    a = PcConnecter{ClientName: "pc"}
    a.Connect()
    fmt.Println(a.Name())
    Disconnect(a)
}

改造Disconnect方法

现在的Disconnect方法.我们并不知道是那个设备断开了链接

type USB interface {
    //定义一个Name方法, 返回值是string
    Name() string
    Connecter
}

//单独定义连接接口
type Connecter interface {
    Connect()
}

//手机接口
type PcConnecter struct {
    ClientName string
}

func (pc PcConnecter) Name() string {
    return pc.ClientName
}

func (pc PcConnecter) Connect() {
    fmt.Println("connected: ", pc.ClientName)
}

//关闭连接
func Disconnect(usb USB) {
    //判断是否是 PcConnecter
    if pc, ok := usb.(PcConnecter); ok {
        fmt.Println("Disconnect", pc.ClientName)
        return
    }
    fmt.Println("Unknown decive.")
}

func main() {
    var a USB
    a = PcConnecter{ClientName: "pc"}
    a.Connect()
    fmt.Println(a.Name())
    Disconnect(a)
}
connected:  pc
pc
Disconnect pc

go语言中都实现了空接口 ?

因为go中没有继承这一说法.. 如果你的一个结构体 只要满足了这个接口的规则.. 那么你就是实现了他

继续改造Disconnect(type switch方式)

因为所有的结构体都是默认实现的空接口

func Disconnect(usb interface{}) {
    //用switch判断更加高效
    //这里我们让go语言自己去猜测usb是什么类型
    switch v := usb.(type) {
        case PcConnecter:
            fmt.Println("Disconnect", v.ClientName)
        default:
            fmt.Println("Unknown decive.")
    }
}

接口间的转换

  • 转换只能发生在 超集的接口转换到子集的接口
  • 比如我们这里的USB接口可以转换到Connecter
  • Connecter却不能转换到USB 因为USB中拥有Connecter的所有属性.而Connecter却没有
type USB interface {
    //定义一个Name方法, 返回值是string
    Name() string
    Connecter
}

//单独定义连接接口
type Connecter interface {
    Connect()
}

//手机接口
type PcConnecter struct {
    ClientName string
}

func (pc PcConnecter) Name() string {
    return pc.ClientName
}

func (pc PcConnecter) Connect() {
    fmt.Println("connected: ", pc.ClientName)
}

//关闭连接
func Disconnect(usb interface{}) {
    switch v := usb.(type) {
    case PcConnecter:
        fmt.Println("Disconnect", v.ClientName)
    default:
        fmt.Println("Unknown decive.")
    }
}

func main() {
    //PcConnecter是实现的USB接口
    pc := PcConnecter{ClientName: "pc"}
    //申明a是Connecter
    var a Connecter
    //把pc转到Connecter并赋值给a
    a = Connecter(pc)
    //调用
    a.Connect()
    Disconnect(a)
    //如果这里去调用Name已经是会报错的..因为Connecter是没有Name方法的
    //a.Name()
}
connected:  pc
Disconnect pc

子集转到超级会报错


type USB interface {
    //定义一个Name方法, 返回值是string
    Name() string
    Connecter
}

//单独定义连接接口
type Connecter interface {
    Connect()
}

//手机接口
type PcConnecter struct {
    ClientName string
}

type TVConnecter struct {
    ClientName string
}

//TVConnecter只实现了Connecter,没有实现USB
func (tv TVConnecter) Connect() {
    fmt.Println("connected: ", tv.ClientName)
}

func (pc PcConnecter) Name() string {
    return pc.ClientName
}

func (pc PcConnecter) Connect() {
    fmt.Println("connected: ", pc.ClientName)
}

//关闭连接
func Disconnect(usb interface{}) {
    switch v := usb.(type) {
    case PcConnecter:
        fmt.Println("Disconnect", v.ClientName)
    default:
        fmt.Println("Unknown decive.")
    }
}

func main() {
    //PcConnecter是实现的USB接口
    tv := TVConnecter{ClientName: "tv"}
    //申明a是Connecter
    var a USB
    //把pc转到Connecter并赋值给a
    a = USB(tv)
    //调用
    a.Connect()
    Disconnect(a)
}
# command-line-arguments
./basic_structure.go:55:9: cannot convert tv (type TVConnecter) to type USB:
    TVConnecter does not implement USB (missing Name method)

因为 TVConnecter只实现了Connecter,没有实现USB 所以转换会报错

将对象赋值给接口时, 会发生拷贝

将对象赋值给接口时, 会发生拷贝, 而接口内部储存的是指向这个复制品的指针, 既无法修改复制品的状态, 也无法获取指针

type USB interface {
    //定义一个Name方法, 返回值是string
    Name() string
    Connecter
}

//单独定义连接接口
type Connecter interface {
    Connect()
}

//手机接口
type PcConnecter struct {
    ClientName string
}

func (pc PcConnecter) Name() string {
    return pc.ClientName
}

func (pc PcConnecter) Connect() {
    fmt.Println("connected: ", pc.ClientName)
}

//关闭连接
func Disconnect(usb interface{}) {
    switch v := usb.(type) {
    case PcConnecter:
        fmt.Println("Disconnect", v.ClientName)
    default:
        fmt.Println("Unknown decive.")
    }
}

func main() {
    //PcConnecter是实现的USB接口
    pc := PcConnecter{ClientName: "pc"}
    //申明a是Connecter
    var a Connecter
    //把pc转到Connecter并赋值给a
    a = Connecter(pc)
    //调用
    a.Connect()
    Disconnect(a)
    //这里我们修改了pc的名称, 
    pc.ClientName = "new pc"
    //然后用a去调用,因为这里是值拷贝..所以还是pc
    a.Connect()
    Disconnect(a)
    //这里我们用pc调用,这里就是new pc了
    pc.Connect()
    Disconnect(pc)
}
connected:  pc
Disconnect pc
connected:  pc
Disconnect pc
connected:  new pc
Disconnect new pc

只有当接口存储的类型和对象都为nil时 接口才等于nil

func main() {
    //a本身就是个nil
    var a interface{}
    fmt.Println((a == nil))
    //这里p是作为一个指针指向nil,本身并不是为nil,也就是p存储的类型是一个指针
    var p *int = nil
    a = p
    fmt.Println((a == nil))
}
true
false