Go接口

接口

接口和面向对象语言有那么一点像,但又不完全像,接口中会定义一些方法,这些方法也是需要实现的
但是接口同时也是一个数据类型,它可以被传递

隐式实现

实现接口中的所有方法不需要用 “implements” 关键字
这样接口的实现就可以出现在任何一个包中,不必提前准备

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
// 定义接口,需要实现 fun() 方法
type MyInt interface {
fun()
}

// 定义一个结构体
type vertex struct {
S string
}

// 也是一个自定义类型
type MyFloat float64

func (v vertex) fun() {
fmt.Printf("用vertex实现了方法\n")
fmt.Println(v)
}

func (f MyFloat) fun() {
fmt.Printf("用MyFloat实现了方法\n")
fmt.Println(f)
}

func main() {
// 用 vertex 实现一个接口
var i MyInt
i = vertex{"hehe"}
i.fun()

// 用 MyFloat 实现一个接口
var j MyInt = MyFloat(20)
j.fun()
}

在上面的代码中,一个接口实例化出了两个变量 i, j
i 使用 vertex 结构实现了 fun()方法
j 使用 MyFloat 结构实现了 fun()方法
就因为他们在实现的时候,选择的实现方式不同,他们的 fun() 方法也就不一样
运行结果:

1
2
3
4
用vertex实现了方法
{hehe}
用MyFloat实现了方法
20

空接口

指定了零个方法的接口值被称为空接口

1
interface{}

空接口可保存任何类型的值,因为每个类型都至少实现了零个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
var i interface{}
describe(i)

i = 42
describe(i)

i = "hello"
describe(i)
}

func describe(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}

空接口有一个好处就是谁都可以用,连空值也可以
空接口还可以和类型断言一起使用,可以用来做类型选择

类型断言

用来判断一个值的类型的接口
用法如下:

1
t, ok := i.(T)

如果 i 确实是 T 类型的值,那么 t 就会获取到 i 的值 true
如果 i 不是 T 类型的值, 那么 t 会获取到 0, ok 获取到 false
第二个值 ok 是可以不写的,只是在类型判断错误的时候程序会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
var i interface{} = "hello"

s := i.(string)
fmt.Println(s)

s, ok := i.(string)
fmt.Println(s, ok)

f, ok := i.(float64)
fmt.Println(f, ok)

f = i.(float64) // 报错
fmt.Println(f)

类型选择

类型选择和类型断言很像,只是把 i.(T) 中的 T 换成了关键字 type

1
2
3
4
5
6
7
8
switch v := i.(type) {
case int:
fmt.Printf("Type is int value is %v\n", v)
case string:
fmt.Printf("Type is string value is %v\n", v)
default:
fmt.Printf("I don't know type %T!\n", v)
}

Stringer 接口

要求是 fmt 包中的 module 才有用
接口长这样

1
2
3
type Stringer interface {
String() string
}

Stringer 接口我感觉就相当于 Java 中的重写 ToString 方法
通过重写 Stringer 接口中的 String() 方法,就可以按照自己的格式输出了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import "fmt"

type CodeLanguage struct {
Language string
Birth int
}

func (c CodeLanguage) String() string {
return fmt.Sprintf("%v (%v year)", c.Language, c.Birth)
}

func main() {
a := CodeLanguage{"Go", 2009}
z := CodeLanguage{"C", 1972}
fmt.Println(a, z)
}

错误

Go 程序使用 error 值来表示错误状态
和fmt.Stringer 类似,error类型是一个内建接口,长这样:

1
2
3
type error interface {
Error() string
}

也就是说可以重写某些自定义类型的 Error() 方法,达到如果出错可以快速找到那里出错了的目的
函数会返回一个 error 值,调用它的时候可以判断这个 error 的值是否为 nil,为 nil 表示没有出错(理解为错误为nil)

比如下面的 Go 指南里面的题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type ErrNegativeSqrt float64

// 重写了 ErrNegativeSqrt 的 Error() 方法
func (e ErrNegativeSqrt) Error() string{
return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}

// 计算开方,返回一个 float64 和一个 error 值
func Sqrt(x float64) (float64, error) {
//小于零的值不能开方,返回0和一个不为 nil 的 error 值
if x < 0{
return 0, ErrNegativeSqrt(x)
}
// 正常开方,返回计算后的值和一个值为 nil 的error值
return math.Sqrt(x),nil
}

func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}

学了一天的 Go 了,感觉很快基础就可以学完了,可以开始学点高端的东西了 (,,・ω・,,)
果然还是看文档学东西快,看别人讲其实就是别人把文档改讲的东西讲了一遍,这样还容易被别人的错误理解给误导浪费时间
不如自己直接学一手知识 (*‘ v`*)