1 它是什么
说背景和地位
代码语言:javascript复制摘录自《go语言编程》
接口在Go语言有着至关重要的地位。如果说goroutine和channel 是支撑起Go语言的并发模型的基石,让Go语言在如今集群化与多核化的时代成为一道极为亮丽的风景,那么接口是Go语言整个类型系统的基石,让Go语言在基础编程哲学的探索上达到前所未有的高度。
Go语言在编程哲学上是变革派,而不是改良派。这不是因为Go语言有goroutine和channel,而更重要的是因为Go语言的类型系统,更是因为Go语言的接口。 Go语言的编程哲学因为有接口而趋近完美。
Go语言的非侵入式接口,看似只是做了很小的文法调整,实则影响深远。
侵入式接口,“侵入式”的主要表现在于实现类需要明确声明自己实现了某个接口。
一句话说明
接口提供了一种方式来说明对象的行为:如果谁能搞定这件事,它就可以用在这儿。
接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。
所以一句话来说,接口是使用方法的抽象。
2 为什么需要它
接口是使用方法的抽象,使当前程序可以更聚焦在方法的应用,不关心该方法的具体实现。
3 怎么用
接口赋值
接口定义:
代码语言:javascript复制type Integer int
func (a Integer) Less(b Integer) bool {
return a < b
}
func (a *Integer) Add(b Integer) {
*a = b
}
type LessAdder interface {
Less(b Integer) bool
Add(b Integer)
}
接口赋值,将实例赋值给接口:
代码语言:javascript复制var a Integer = 1
var b LessAdder = &a
接口查询
代码语言:javascript复制var file1 Writer = ...
if file5, ok := file1.(two.IStream); ok {
...
}
Writer接口的实现实例file1,是否实现了 two.IStream 接口,如果实现了则执行代码。
类型查询
在 Go 语言中,还可以更加直截了当地询问接口指向的对象实例的类型。
利用反射也可以进行类型查询,详情可参阅reflect.TypeOf()方法。
代码语言:javascript复制var v1 interface{} = ...
switch v := v1.(type) {
case int: // 现在v的类型是int
case string: // 现在v的类型是string
...
}
接口组合
代码语言:javascript复制type ReadWriter interface {
Reader
Writer
}
这个接口组合了Reader和Writer两个接口,它完全等同于如下写法:
代码语言:javascript复制type ReadWriter interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}
4 示例
典型示例 关注使用,不操心接口实现。来自于《GO示例学》。
// 这里定义了一个最基本的表示几何形状的方法的接口
代码语言:javascript复制type geometry interface {
area() float64
perim() float64
}
// 这里不管正方形或者长方形怎么去实现自己的接口,但最后使用部分,直接用接口方法就好了。
代码语言:javascript复制func measure(g geometry) {
fmt.Println(g)
fmt.Println(g.area())
fmt.Println(g.perim())
}
5 空接口 Any类型
由于Go语言中任何对象实例都满足空接口interface{},所以interface{}看起来像是可以指向任何对象的 Any 类型,如下:
代码语言:javascript复制var v1 interface{} = 1 // 将int类型赋值给interface{}
var v2 interface{} = "abc" // 将string类型赋值给interface{}
var v3 interface{} = &v2 // 将*interface{}类型赋值给interface{}
var v4 interface{} = struct{ X int }{1}
var v5 interface{} = &struct{ X int }{1}
当函数可以接受任意的对象实例时,我们会将其声明为interface{},最典型的例子是标准库 fmt 中 PrintXXX 系列的函数,例如:
传入参数是 可变数量的 任意类型。
代码语言:javascript复制func Printf(fmt string, args ...interface{})
func Println(args ...interface{})
总体来说, 我们刚开始对 interface{} 一无所知,但可以通过接口查询和类型查询逐步了解它。
6 小结
总结,接口是使用方法的抽象,使当前程序可以更聚焦在方法的应用,不关心该方法的具体实现。后续的应用程序在做具体实现时,再去完善具体实现,不会影响之前程序已经定义好的逻辑。
更通俗地来讲,接口的定义,就像是设置了一个岗位,描述了岗位的职责;,一些规章可以先根据岗位职责来制定,不关心具体是谁来任职这个岗位。当应用程序开始执行的时候,需要给接口传入一个实例,相当于企业开始运营时再把一个人放到这个岗位。
另外空接口 interface{} 作为 Any 类型也有很广到应用。