一、接口的概念
- 接口是一种抽象类型,是对其他类型行为的概括与抽象,从语法角度来看,接口是一组方法定义的集合。
- 很多面向对象的语言都有接口这个概念,但Go语言接口的独特之处在于它是隐式实现。
- 对于一个具体的类型,无须声明它实现了哪些接口,只要提供接口所必需的方法即可。
- 这种设计让编程人员无须改变已有类型的实现就可以为这些类型创建新的接口——对于那些不能修改包的类型,这一点特别有用。
二、接口的定义和实现
下面我们直接来看看接口是怎样定义的:
type 接口名 interface {
方法1
方法2
……
}
这个就是接口的定义
接口是由使用者定义,由实现者实现。
假如现在有一个任务:将 www.imooc.com 的首页下载下来(假如是一个很大的工程),我们需要先进行测试,然后再产品上线。
1.实现测试部分:
先定义一个接口:
type Retriever interface{Get(cur string)string
}
下面来看看接口是如何实现的:
我们在另一个目录中实现Get()方法(接口的实现)
测试部分:
package mock
//定义一个结构体
type Retrievers struct{
Context string
}
//实现接口
func (r Retrievers)Get(cur string)string{return r.Context
}
2.产品上线部分:
然后我们再去目录下对Get()方法的实现:
package real
import ("net/http""net/http/httputil" "time")
//定义一个结构体
type Retrievers struct{
UserAgent string
TimeOut time.Duration
}
//Get接口的实现
func (r Retrievers)Get(cur string)string{
re, err :=http.Get(cur)if err != nil{panic(err)}
result, err :=httputil.DumpResponse(re, true)if err != nil{panic(err)}return string(result)
}
现在回到主程序中:
package main
import ("fmt""interfacetest/retriever/mock" "interfacetest/retriever/real")
type Retriever interface {Get(cur string) string
}
//接口由使用者定义,接口的实现其实就是对象函数(方法)的实现
//golang中duck type
func download(r Retriever) string{//下载https://www.imooc.com网页return r.Get("https://www.imooc.com")
}
//主函数
func main() {
//mock.Retrievers{}表示来自于我们实现的mock包var q = mock.Retrievers{"my name"}
fmt.Println(downloda(q)) //此时输出的是my name
//当测试部分通过过后就可以产品上线了//real.Retrievers{}表示来自于我们实现的real包var r Retriever= real.Retrievers{UserAgent: "hhh", TimeOut: 3}
fmt.Println(download(r)) //这时就可以获取https://www.imooc.com的首页内容
三、接口的值类型
判断接口值的类型有三种方法:
fmt.Printf("%T, %v\n", x, x)
- 例如:
func main() {
var q = mock.Retrievers{"my name"}
var r Retriever= real.Retrievers{UserAgent: "hhh", TimeOut: 3}
fmt.Printf("%T, %v\n", r, r)
fmt.Printf("%T, %v\n", q, q)
- 输出为:
real.Retrievers, {hhh 3ns}
mock.Retrievers, {my name}
- switch
- 例如:
func inspect(r Retriever){
fmt.Printf("%T, %v\n", r, r)
switch v := r.(type){
case mock.Retrievers:
fmt.Println("mock.Retrievers:",v.Context)
case real.Retrievers:
fmt.Println("real.Retrievers:", v.UserAgent)
case *mock.Retrievers:
fmt.Println("*mock.Retrievers", v.Context)}
}
- assertion
- 例如:
//使用assertion断言类型
if note , ok := r.(real.Retrievers);ok {
fmt.Println(note.UserAgent)
} else{
fmt.Println("this not real Retrieers")
- 接口也可以是指针
func (r *Retrievers)Get(cur string)string{
return r.Context
}
func main() {
//&取地址
var q = &mock.Retrievers{"my name"}
四、接口的组合
在Go语言中,可以在接口A中组合其它的一个或多个接口(如接口B、C),这种方式等价于在接口A中添加接口B、C中声明的方法。
代码如下:
//接口中可以组合其它接口,这种方式等效于在接口中添加其它接口的方法
type Reader interface {
read()
}
type Writer interface {
write()
}
//定义上述两个接口的实现类
type MyReadWrite struct{}
//read接口的实现
func (mrw *MyReadWrite) read() {
fmt.Println("MyReadWrite...read")
}
//write接口的实现
func (mrw *MyReadWrite) write() {
fmt.Println("MyReadWrite...write")
}
//定义一个接口,组合了上述两个接口
type ReadWriter interface {
Reader
Writer
}
//上述接口等价于:
type ReadWriterV2 interface {
read()
write()
}
//ReadWriter和ReadWriterV2两个接口是等效的,因此可以相互赋值
func interfaceTest() {
mrw := &MyReadWrite{}
//mrw对象实现了read()方法和write()方法,因此可以赋值给ReadWriter和ReadWriterV2 var rw1 ReadWriter = mrw
rw1.read()
rw1.write()
fmt.Println("------")
var rw2 ReadWriterV2 = mrw
rw2.read()
rw2.write()
//同时,ReadWriter和ReadWriterV2两个接口对象可以相互赋值
rw1 = rw2
rw2 = rw1
}