Go 语言里有非常灵活的 接口 概念,通过它可以实现很多面向对象的特性。接口提供了一种方式来 说明 对象的行为。接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。

  1. type Namer interface {
  2. Method1(param_list) return_type
  3. Method2(param_list) return_type
  4. ...
  5. }

按照约定,只包含一个方法的接口的名字由方法名加 [e]r 后缀组成,例如 PrinterReaderWriterLoggerConverter 等等。还有一些不常用的方式(当后缀 er 不合适时),比如 Recoverable,此时接口名以 able 结尾,或者以 I 开头(像 .NETJava 中那样)。
Go 语言中的接口都很简短,通常它们会包含 0 个、最多 3 个方法。
Go 语言中接口可以有值,一个接口类型的变量或一个 接口值var ai Namerai 是一个多字(multiword)数据结构,它的值是 nil。它本质上是一个指针,虽然不完全是一回事。指向接口值的指针是非法的,它们不仅一点用也没有,还会导致代码错误。
类型(比如结构体)实现接口方法集中的方法,每一个方法的实现说明了此方法是如何作用于该类型的:即实现接口,同时方法集也构成了该类型的接口。实现了 Namer 接口类型的变量可以赋值给 ai (接收者值),此时方法表中的指针会指向被实现的接口方法。当然如果另一个类型(也实现了该接口)的变量被赋值给 ai,这二者(指针和方法实现)也会随之改变。

  • 类型不需要显式声明它实现了某个接口:接口被隐式地实现。
  • 多个类型可以实现同一个接口
  • 实现某个接口的类型(除了实现接口方法外)可以有其他的方法
  • 一个类型可以实现多个接口
  • 接口类型可以包含一个实例的引用, 该实例的类型实现了此接口(接口是动态类型)
    1. package main
    2. import "fmt"
    3. type Shaper interface {
    4. Area() float32
    5. }
    6. type Square struct {
    7. side float32
    8. }
    9. func (sq *Square) Area() float32 {
    10. return sq.side * sq.side
    11. }
    12. func main() {
    13. sq1 := new(Square)
    14. sq1.side = 5
    15. var areaIntf Shaper
    16. areaIntf = sq1
    17. // shorter,without separate declaration:
    18. // areaIntf := Shaper(sq1)
    19. // or even:
    20. // areaIntf := sq1
    21. fmt.Printf("The square has area: %f\n", areaIntf.Area())
    22. }

    1、接口嵌套接口

    一个接口可以包含一个或多个其他的接口。 ```go type ReadWrite interface { Read(b Buffer) bool Write(b Buffer) bool }

type Lock interface { Lock() Unlock() }

type File interface { ReadWrite Lock Close() }

  1. <a name="Qj13A"></a>
  2. # 2、类型断言
  3. 只有接口类型的变量才有类型断言。一个接口类型的变量 `varI` 中可以包含任何类型的值,必须有一种方式来检测它的 **动态** 类型,即运行时在变量中存储的值的实际类型。在执行过程中动态类型可能会有所不同,但是它总是可以分配给接口变量本身的类型。通常我们可以使用 **类型断言** 来测试在某个时刻 `varI` 是否包含类型 `T` 的值。
  4. ```go
  5. v := varI.(T) // unchecked type assertion

类型断言可能是无效的,虽然编译器会尽力检查转换是否有效,但是它不可能预见所有的可能性。如果转换在程序运行时失败会导致错误发生。更安全的方式是使用以下形式来进行类型断言:

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

如果转换合法,vvarI 转换到类型 T 的值,ok 会是 true;否则 v 是类型 T 的零值,okfalse,也没有运行时错误发生。

3、类型判断

接口变量的类型也可以使用一种特殊形式的 switch 来检测:type-switch

  1. switch t := areaIntf.(type) {
  2. case *Square:
  3. fmt.Printf("Type Square %T with value %v\n", t, t)
  4. case *Circle:
  5. fmt.Printf("Type Circle %T with value %v\n", t, t)
  6. case nil:
  7. fmt.Printf("nil value: nothing to check?\n")
  8. default:
  9. fmt.Printf("Unexpected type %T\n", t)
  10. }

变量 t 得到了 areaIntf 的值和类型, 所有 case 语句中列举的类型(nil 除外)都必须实现对应的接口。
可以用 type-switch 进行运行时类型分析,但是在 type-switch 不允许有 fallthrough
如果仅仅是测试变量的类型,不用它的值,那么就可以不需要赋值语句。

  1. switch areaIntf.(type) {
  2. case *Square:
  3. // TODO
  4. case *Circle:
  5. // TODO
  6. default:
  7. // TODO
  8. }

4、空接口

空接口不包含任何方法,它对实现不做任何要求。

  1. type Any interface {}

任何其他类型都实现了空接口,anyAny 是空接口一个很好的别名或缩写。
空接口类似 Java/C# 中所有类的基类: Object 类,二者的目标也很相近。

  1. package main
  2. import "fmt"
  3. var i = 5
  4. var str = "ABC"
  5. type Person struct {
  6. name string
  7. age int
  8. }
  9. type Any interface{}
  10. func main() {
  11. var val Any
  12. val = 5
  13. fmt.Printf("val has the value: %v\n", val)
  14. val = str
  15. fmt.Printf("val has the value: %v\n", val)
  16. pers1 := new(Person)
  17. pers1.name = "Rob Pike"
  18. pers1.age = 55
  19. val = pers1
  20. fmt.Printf("val has the value: %v\n", val)
  21. switch t := val.(type) {
  22. case int:
  23. fmt.Printf("Type int %T\n", t)
  24. case string:
  25. fmt.Printf("Type string %T\n", t)
  26. case bool:
  27. fmt.Printf("Type boolean %T\n", t)
  28. case *Person:
  29. fmt.Printf("Type pointer to Person %T\n", t)
  30. default:
  31. fmt.Printf("Unexpected type %T", t)
  32. }
  33. }