Go教程:20-interface接口
1. 什么是interface
Go 语言不是一种 “传统” 的面向对象编程语言:它里面没有类和继承的概念,
Go 语言里有非常灵活的 接口 概念,通过它可以实现很多面向对象的特性, 接口提供了一种方式来说明对象的行为,
- 接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:
- 接口方法没有被实现(它们是抽象的),
- 接口里也不能包含变量,
interface定义语法
type Interfacer interface {
Method1(param_list) return_type
Method2(param_list) return_type
...
}
1.1 Golang interface 惯例
- 命名惯例:接口的名字由方法名加 [e]r 后缀组成, 例如 Printer,Reader,Writer,Logger,Converter 等等
- Go 语言中的接口都很简短,通常它们会包含 0 个,最多 3 个方法,
- Go 语言中接口可以有值,一个接口类型的变量或一个 接口值
- 类型不需要显式声明它实现了某个接口:接口被隐式地实现,多个类型可以实现同一个接口,
- 一个类型可以实现多个接口,
1.2 多态Go语言实现
多态是面向对象编程中一个广为人知的概念: 根据当前的类型选择正确的方法, 或者说:同一种类型在不同的实例上似乎表现出不同的行为.
package main
import "fmt"
type Shaper interface {
Area() float32
}
type Square struct {
side float32
}
func (sq *Square) Area() float32 {
return sq.side * sq.side
}
type Rectangle struct {
length, width float32
}
func (r Rectangle) Area() float32 {
return r.length * r.width
}
func main() {
r := Rectangle{5, 3} // Area() of Rectangle needs a value
q := &Square{5} // Area() of Square needs a pointer
// shapes := []Shaper{Shaper(r), Shaper(q)}
// or shorter
shapes := []Shaper{r, q}
fmt.Println("Looping through shapes for area ...")
for n, _ := range shapes {
fmt.Println("Shape details: ", shapes[n])
fmt.Println("Area of this shape is: ", shapes[n].Area())
}
}
1.3 接口interface如何产生 更干净,更简单 及 更具有扩展性 的代码
下面是一个更具体的例子:有两个类型 stockPosition 和 car,它们都有一个 getValue() 方法,我们可以定义一个具有此方法的接口 valuable, 接着定义一个使用 valuable 类型作为参数的函数 showValue(),所有实现了 valuable 接口的类型都可以用这个函数,
package main
import "fmt"
type stockPosition struct {
ticker string
sharePrice float32
count float32
}
/* method to determine the value of a stock position */
func (s stockPosition) getValue() float32 {
return s.sharePrice * s.count
}
type car struct {
make string
model string
price float32
}
/* method to determine the value of a car */
func (c car) getValue() float32 {
return c.price
}
/* contract that defines different things that have value */
type valuable interface {
getValue() float32
}
func showValue(asset valuable) {
fmt.Printf("Value of the asset is %f\n", asset.getValue())
}
func main() {
var o valuable = stockPosition{"GOOG", 577.20, 4}
showValue(o)
o = car{"BMW", "M3", 66500}
showValue(o)
}
2. interface 嵌套
一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样,
比如接口 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()
}
3. interface 类型断言
一个接口类型的变量 varI 中可以包含任何类型的值,必须有一种方式来检测它的 动态 类型,即运行时在变量中存储的值的实际类型, 正确安全的类型断言的形势如下:
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,也没有运行时错误发生,
3.1 测试一个值是否实现了某个接口
假定 v 是一个值,然后我们想测试它是否实现了 Stringer 接口,可以这样做:
type Stringer interface {
String() string
}
if sv, ok := v.(Stringer); ok {
fmt.Printf("v implements String(): %s\n", sv.String()) // note: sv, not v
}
4. 类型判断: type-switch
接口变量的类型也可以使用一种特殊形式的 switch 来检测:type-switch.
switch t := areaIntf.(type) {
case *Square:
fmt.Printf("Type Square %T with value %v\n", t, t)
case *Circle:
fmt.Printf("Type Circle %T with value %v\n", t, t)
case nil:
fmt.Printf("nil value: nothing to check?\n")
default:
fmt.Printf("Unexpected type %T\n", t)
}
可以用 type-switch 进行运行时类型分析,但是在 type-switch 不允许有 fallthrough ,嵌套嵌套
5. 空接口interface{}
空接口或者最小接口 不包含任何方法,它对实现不做任何要求. 可以给一个空接口类型的变量 var val interface {} 赋任何类型的值,
package main
import "fmt"
var i = 5
var str = "ABC"
type Person struct {
name string
age int
}
type Any interface{}
func main() {
var val Any
val = 5
fmt.Printf("val has the value: %v\n", val)
val = str
fmt.Printf("val has the value: %v\n", val)
pers1 := new(Person)
pers1.name = "Rob Pike"
pers1.age = 55
val = pers1
fmt.Printf("val has the value: %v\n", val)
switch t := val.(type) {
case int:
fmt.Printf("Type int %T\n", t)
case string:
fmt.Printf("Type string %T\n", t)
case bool:
fmt.Printf("Type boolean %T\n", t)
case *Person:
fmt.Printf("Type pointer to Person %T\n", t)
default:
fmt.Printf("Unexpected type %T", t)
}
}