go interface
Go 语言不是一种 “传统” 的面向对象编程语言:它里面没有类
和继承
的概念。
但是 Go 语言里有非常灵活的接口
概念,通过它可以实现很多面向对象
的特性。
接口
定义了一个 方法的集合
,但是这些方法 不包含实现代码
,它们是 抽象的
,接口里也 不能包含变量
。
定义格式
定义接口的一般格式:
type Namer interface {
Method1(param_list) return_type
Method2(param_list) return_type
...
}
上面的 Namer
就是一个接口类型。
在 Go 语言中 接口
可以有值, 一个 接口类型
的变量或一个 接口值
:var ai Namer
,ai
是一个 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())
}
如果去掉 Shaper
中 Perimeter() 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,areaIntf.Area())
triangle := new(Triangle)
triangle.Set(2, 3)
areaIntf = Shaper(triangle)
fmt.Printf("The triangle has area: %f\n",areaIntf.Area())
}
灵活性
接口的定义是比较灵活的。
假设接口和类型处于不同的包中,只要类型实现了接口中的全部方法,那么它就实现了此接口。
现在我们将 Shaper
的定义放在 shaper 包
下, Rectangle
和 Triangle
的定义放在 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,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
包含了 ReadWrite
和 Lock
的所有方法,它还额外有一个 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
如果 v
是 varI
转换到类型 T
的值,ok
会是 true
;否则 v
是类型 T
的零值,ok
是 false
。
// 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
}