接口类型是对其它类型行为的抽象和概括.
接口类型是由一组方法签名定义的集合. 接口类型不会和特定的实现细节绑定在一起.
Go 语言中的接口类型的独特之处在于它是满足隐式实现的. 也就是说, 我们没必要对于给定的具体类型定义所有满足接口的实现.
接口类型的变量可以保存任何实现了这些方法的值.

  1. // 接口定义
  2. type Abser interface {
  3. Abs() float64
  4. }

类型通过实现一个接口的所有方法来实现该接口.
在内部, 接口值可以看做包含值和具体类型的元组(value, type):

// i 为接口的实现,
fmt.Printf("(%v, %T)\n", i, i)

即便接口实现内的具体值为 nil, 方法仍然会被 nil 接收者调用(不会出现空指针异常).
Interface{} 可代表任意类型. 当你不确定传入的数据是什么类型时, 可以使用它.

接口约定

当你看到一个接口类型的值时, 你不知道它是什么, 唯一知道的就是可以通过它的方法来做什么.

接口类型

接口类型具体描述了一系列方法的集合, 一个实现了这些方法的具体类型是这个接口类型的实例.
对于任意数据类型, 只要它的方法集合中完全包含了一个接口的全部特征(即全部的方法), 那么它就一定是这个接口的实现类型. 这是一种无侵入式的接口实现方式. 称为 “Duck typing(鸭子类型)”.
接口内嵌: 类似于结构体的匿名嵌入特性, 可以实现接口的组合. 接口之间的组合不会发生 “屏蔽” 现象, 编译器会直接报错.
Go 语言团队鼓励我们声明体量较小的接口, 并建议我们通过这种接口间的组合来扩展程序, 增加程序的灵活性. 小接口可以更加专注地表达某一种能力或某一类特征, 同时也更容易被组合在一起.

实现接口的条件

一个类型如果拥有一个接口需要的所有方法, 那么这个类型就实现了这个接口.
interface{} 被称为空接口类型. 因为空接口对实现它的类型没有要求, 我们可以将任意一个值赋给空接口类型.
空接口被用来处理未知类型的值.
interface{} 也可用来定义接口.

var i interface{} = nil

接口值

一个接口的值由两个部分组成, 一个具体的类型和那个类型的值(unsafe.Pointer). 它们被称为接口的动态类型和动态值.
接口的动态值本身就是指针, 因此在将参数定义为接口类型时, 其本身就是一个对指针的引用, 不需要再将其声明为指针了.
在 Go 中, 变量总是被一个定义明确的值初始化, 即使接口类型也不例外. 对于一个接口的零值就是它的类型和值的部分都是 nil.
调用一个空接口值上的任意方法都会引起 panic.
当接口类型的值是可比较的类型时接口值才是可进行比较的. 仅当接口的类型和值都相等时他们才相等. 接口值可用在 map 的键或 switch 语句中.
接口变量被赋予动态值的时候, 其内部存储包含了动态值的副本和一个指向类型信息的指针. 所以即使给接口赋予 nil 值, 接口变量也不会为 nil.

类型断言

在 Go 中, interface{} 代表空接口, 任何类型都是它的实现类型.
一对不包括任何东西的花括号, 除了可以代表空的代码块外, 还可以用于表示不包含任何内容的数据类型. 如 struct{}, interface{}.

类型断言提供了访问接口值底层具体值的方式.

t := i.(T) // 若 i 并未保存 T 类型的值, 该语句就会触发一个恐慌
t, ok := i.(T)

该语句断言接口 i 保存了具体类型 T, 并将其底层类型为 T 的值赋予变量 t.

/*
语法:
    // 安全类型断言
    <目标类型的值>, <布尔参数> := <表达式>.(目标类型) 
    // 非安全类型断言
    <目标类型的值> := <表达式>.(目标类型)
*/
var i interface{} = `s`
fmt.Printf("%T\n", i)
// 断言 j 为 int 类型, 值为 i.
if j, ok := i.(int); ok {
    fmt.Printf("j is %T\n", j)
}
// 断言 j 为 string 类型, 值为 i.
if j, ok := i.(string); ok {
    fmt.Printf("j is %T\n", j)
}

基于 switch 的类型断言:

// x.(type) 只可用于 switch 语句
var i interface{} = `s`
switch x := i.(type) {
default:
    fmt.Printf("%s\n", x)
}
switch x.(type) {
    case nil:
    case int:
    default:
}

// 函数参数使用 interface{} 类型可以接收任意类型的参数
pt(1)
pt(`s`)
pt(.1)
func pt(x interface{}) {
    switch t := x.(type) {
    default:
        fmt.Printf("%v => %T\n", x, t)
    }
}

常见接口

  • error 类型是一个内建接口. Go 程序使用 error 值来表示错误状态.
  • fmt.Stringer: 返回描述自己的字符串. 很多包都通过此接口打印值.