go 接口

fengfengdiandia · · 1263 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

go interface

Go 语言不是一种 “传统” 的面向对象编程语言:它里面没有继承的概念。

但是 Go 语言里有非常灵活的接口概念,通过它可以实现很多面向对象的特性。

接口 定义了一个 方法的集合,但是这些方法 不包含实现代码,它们是 抽象的,接口里也 不能包含变量

定义格式

定义接口的一般格式:

type Namer interface {
    Method1(param_list) return_type
    Method2(param_list) return_type
   ...
}

上面的 Namer 就是一个接口类型。

在 Go 语言中 接口 可以有值, 一个 接口类型 的变量或一个 接口值var ai Namerai 是一个 multiword 数据结构,它的值是 nil
它本质上是一个 指针,虽然不完全是一回事。指向接口值的指针是非法的,会导致代码错误。

类型(比如结构体)实现接口方法集中的方法,实现了 Namer 接口类型的变量可以赋值给 ai,此时方法表中的指针会指向被实现的接口方法。

实现某个接口的类型,除了实现接口的方法外,还可以有自己的方法。

package main

import "fmt"

type Shaper interface {
    Area() float64
    //  Perimeter() float64
}

type Rectangle struct {
    length float64
    width  float64
}

// 实现 Shaper 接口中的方法
func (r *Rectangle) Area() float64 {
    return r.length * r.width
}

// Set 是属于 Rectangle 自己的方法
func (r *Rectangle) Set(l float64, w float64) {
    r.length = l
    r.width = w
}

func main() {
    rect := new(Rectangle)
    rect.Set(2, 3)
    areaIntf := Shaper(rect)
    fmt.Printf("The rect has area: %f\n", areaIntf.Area())
}

如果去掉 ShaperPerimeter() float64 的注释,编译的时候会遇到下面的错误,这是因为 Rectangle 没有实现 Perimeter() 方法。

cannot convert rect (type *Rectangle) to type Shaper: *Rectangle does not implement Shaper (missing Perimeter method)

多态

1、多个类型可以实现同一个接口。
2、一个类型可以实现多个接口。

下面我们增加一个类型 Triangle,同样也为它实现 Shaper 接口。

package main

import "fmt"

type Shaper interface {
    Area() float64
    //  Perimeter() float64
}

// ==== Rectangle ====
type Rectangle struct {
    length float64
    width  float64
}

// 实现 Shaper 接口中的方法
func (r *Rectangle) Area() float64 {
    return r.length * r.width
}

// Set 是属于 Rectangle 自己的方法
func (r *Rectangle) Set(l float64, w float64) {
    r.length = l
    r.width = w
}

// ==== Rectangle End ====

// ==== Triangle ====
type Triangle struct {
    bottom float64
    hight  float64
}

func (t *Triangle) Area() float64 {
    return t.bottom * t.hight / 2
}

func (t *Triangle) Set(b float64, h float64) {
    t.bottom = b
    t.hight = h
}

// ==== Triangle End ====

func main() {
    rect := new(Rectangle)
    rect.Set(2, 3)
    areaIntf := Shaper(rect)
    fmt.Printf("The rect has area: %f\n", areaIntf.Area())

    triangle := new(Triangle)
    triangle.Set(2, 3)
    areaIntf = Shaper(triangle)
    fmt.Printf("The triangle has area: %f\n", areaIntf.Area())
}

灵活性

接口的定义是比较灵活的。

假设接口和类型处于不同的包中,只要类型实现了接口中的全部方法,那么它就实现了此接口。

现在我们将 Shaper 的定义放在 shaper 包 下, RectangleTriangle 的定义放在 test 包 下:

[root@ src]# tree
├── test
│   └── test.go
├── main.go
└── shaper
    └── shaper.go
// shaper.go
package shaper

type Shaper interface {
    Area() float64
}
// test.go
package test

// ==== Rectangle ====
type Rectangle struct {
    length float64
    width  float64
}

// 实现 Shaper 接口的方法
func (r *Rectangle) Area() float64 {
    return r.length * r.width
}

// Set 是属于 Rectangle 自己的方法
func (r *Rectangle) Set(l float64, w float64) {
    r.length = l
    r.width = w
}

// ==== Rectangle End ====

// ==== Triangle ====
type Triangle struct {
    bottom float64
    hight  float64
}

func (t *Triangle) Area() float64 {
    return t.bottom * t.hight / 2
}

func (t *Triangle) Set(b float64, h float64) {
    t.bottom = b
    t.hight = h
}

// ==== Triangle End ====
// main.go
package main

import (
    "fmt"
    "shaper"
    "test"
)

func main() {
    rect := new(test.Rectangle)
    rect.Set(2, 3)
    areaIntf := shaper.Shaper(rect)
    fmt.Printf("The rect has area: %f\n", areaIntf.Area())

    triangle := new(test.Triangle)
    triangle.Set(2, 3)
    areaIntf = shaper.Shaper(triangle)
    fmt.Printf("The triangle has area: %f\n", areaIntf.Area())
}

现在运行 main.go 看看结果吧,嗯嗯,没什么问题,^_^

The rect has area: 6.000000
The triangle has area: 3.000000

接口嵌套

一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样。

比如接口 File 包含了 ReadWriteLock 的所有方法,它还额外有一个 Close() 方法。

type ReadWrite interface {
    Read(b Buffer) bool
    Write(b Buffer) bool
}

type Lock interface {
    Lock()
    Unlock()
}

type File interface {
    ReadWrite
    Lock
    Close()
}

类型断言

假如我现在写了一个结构体类型 MyFile 来实现上面的 File 接口,那么我如何知道 MyFile 是否实现了 File 接口呢?

通常我们使用 类型断言 来测试在某个时刻 varI 是否包含类型 T 的值:

if v, ok : = varI.(T) ; ok { // checked type assertion
    Process(v)
    return
}
// varI is not of type T

如果 vvarI 转换到类型 T 的值,ok 会是 true;否则 v 是类型 T 的零值,okfalse

// main.go
package main

import "fmt"

type MyFile struct{}

func (m *MyFile) Read() bool {
    fmt.Printf("Read()\n")
    return true
}

// ...
// 假设我这里相继实现了 Write(), Lock(),Unlock() 和 Close() 方法

func main() {
    my := new(MyFile)
    fIntf := File(my)

    // 看这里,看这里
    if v, ok := fIntf.(*MyFile); ok {
        v.Read()
    }
}

输出结果是:Read()

要是多个类型实现了同一个接口,比如前面的 areaIntf,要如何测试呢?
那就要用 type-switch 来判断了。

type-switch 类型判断

switch t := areaIntf.(type) {
case *Rectangle:
    // do something
case *Triangle:
    // do something
default:
    // do something
}

有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:fengfengdiandia

查看原文:go 接口

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1263 次点击  
加入收藏 微博
1 回复  |  直到 2017-09-02 09:45:29
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传