接口类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import "fmt"

type People struct {

}

type speaker interface {
	say(content string) string
}

func (p *People) say(content string) string{
	return "people say: " + content
}

func main(){
	var p People
	var s speaker
	s = &p   // 注意取地址符
	fmt.Println(s.say("I love you"))
}

实现接口的条件

  • 接口A可以赋值给接口B,前提是接口B实现了接口A的某些方法
  • 空接口类型interface{}: 任意类型都可以被空接口赋值

接口值

  • 接口值由两部分组成:
    • 具体的类型(动态类型)
    • 该类型的值(动态值)
  • 一个接口的零值就是它的类型和值的部分都是nil

在go中,类型是编译期的概念,因此类型不是一个值。
一些提供每个类型信息的值被称为类型描述符,比如名称和方法。

1
2
3
4
5
var w io.Writer        // type=nil, value=nil
w = os.Stdout          // 动态类型被设置为*os.File指针的类型描述符,它的动态值持有os.Stdout的拷贝
w = new(bytes.Buffer)  // type=*bytes.Buffer, 动态值是一个指向新分配的缓冲区的指针
w = nil

接口值是可比较的,所以可以用作map的key和switch的case
但如果两个接口值的动态类型相同,但是这个动态类型是不可比较的(比如切片),将它们进行比较就会失败并且panic:

1
2
var x interface{} = []int{1, 2, 3}
fmt.Println(x == x) // panic: comparing uncomparable type []int

使用fmt.Printf("%T\n")获得接口值的动态类型

1
2
3
4
5
6
var w io.Writer
fmt.Printf("%T\n", w) // "<nil>"
w = os.Stdout
fmt.Printf("%T\n", w) // "*os.File"
w = new(bytes.Buffer)
fmt.Printf("%T\n", w) // "*bytes.Buffer"

警告:一个包含nil指针的接口不是nil接口

一个不包含任何值的nil接口值和一个刚好包含nil指针的接口值是不同的

sort.Interface接口

在go中给自定义类型排序,需要实现sort.Interface接口中的几个方法:

1
2
3
4
5
type Interface interface {
    Len() int
    Less(i, j int) bool // i, j are indices of sequence elements
    Swap(i, j int)
}

一个例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import (
	"fmt"
	"sort"
)

type Point struct{
	x, y int
}

type PointList [7]*Point

func (pl *PointList) Len() int{
	return len(pl)
}

func (pl *PointList) Less(i, j int) bool{
	if pl[i].x < pl[j].x {
		return true
	} else if pl[i].x == pl[j].x {
		return pl[i].y < pl[j].y
	}else {
		return false
	}
}

func (pl *PointList) Swap(i, j int){
	pl[i], pl[j] = pl[j], pl[i]
}

func main(){
	var pli PointList = PointList{
		{1, 2},
		{3, 4},
		{5, 6},
		{1, 1},
		{3, 2},
		{0, 0},
		{1, 3},
	}

	sort.Sort(&pli)

	for _, x := range pli{
		fmt.Println(x)
	}
}

注意拷贝花销,所以所有的地方都传递指针。

http.Handler接口

error接口

类型断言

  • 类型断言是一个使用在接口值上的操作。
  • 语法上它看起来像x.(T)被称为断言类型,这里x表示一个接口的类型和T表示一个类型。
  • 一个类型断言检查它操作对象的动态类型是否和断言的类型匹配。
  • 两种可能
    • 第一种,如果断言的类型T是一个具体类型,然后类型断言检查x的动态类型是否和T相同。如果这个检查成功了,类型断言的结果是x的动态值,当然它的类型是T
    • 第二种,如果相反地断言的类型T是一个接口类型,然后类型断言检查是否x的动态类型满足T。如果这个检查成功了,动态值没有获取到;这个结果仍然是一个有相同动态类型和值部分的接口值,但是结果为类型T。

基于类型断言区别错误类型

通过类型断言询问行为

类型分支

一些建议

接口只有当有两个或两个以上的具体类型必须以相同的方式进行处理时才需要