上一篇我们介绍了duck typing概念,主要讲述GO语言中,如何解决duck typing中编译器不提示错误,和没有duck typing的Java灵活性不够问题.
如何定义一个接口
GO中定义接口可以使用关键字interface
type Retriever interface { //使用interface关键字
Get(url string)string //接口的方法
}
接口的实现
然后定义一个函数,参数为我们定义的Retriever
func download(r Retriever) string {//参数为 Retriever 接口
return r.Get("http://www.imooc.com")
}
所有实现了Retriever接口的结构体都可以传入函数.
定义一个结构体
package mook
type Retriever struct {
Contents string
}
func (r Retriever) Get(url string) string {
return r.Contents
}
可以看到次结构体,在形式上,与我们定义的Retriever interface
没有任何联系.只是里面有一个Get方法,参数与返回值与接口中定义的方法一样.
测试:
func main() {
var r Retriever = mook.Retriever{"this is a fake imook.com"}
s := download(r)
fmt.Println(s)
}
看到是可以打印的,即在接口的实现上,GO语言是有实现者来控制的.
这样做的好处?我们再来实验一下把mook.Retriever
中Get方法删除.可以看到调用download函数时,会报错.这是在编译器时就报错,对于编程人员非常的友好.不至于在运行时报错.具有了Java接口定义的特性,但是在实现上又与接口无关,也就是说,接口的变化是不会强制影响其他实现者的.只有在实现者使用其改变的接口地方需要修改.
实现一个HTTP请求的Retriever
package real
import (
"net/http"
"net/http/httputil"
"time"
)
type Retriever struct {
UserAgent string
TimeOut time.Duration
}
func (r Retriever) Get(url string) string {
resp, err := http.Get(url)
if err!=nil {
panic(err) //这里我们遇到错误就panic其实是不对的,
//后面会讲到正确处理错误的方式
}
result, err := httputil.DumpResponse(resp, true)
resp.Body.Close()
if err!=nil {
panic(err)
}
return string(result)
}
接口的类型判断
接口类型如何去判断?在Java中是使用instanceof
来判断接口是否是指定类型.在GO语言中有两种方式.
- type switch
- type assertion
//type switch
switch v:=r.(type) {
case mook.Retriever:
fmt.Println("Contents:",v.Contents)
case real2.Retriever:
fmt.Println("UserAgent:",v.UserAgent)
}
//type assertion
if retriever,ok := r.(real2.Retriever);ok{
fmt.Println(retriever.UserAgent)
}else if retriever,ok := r.(mook.Retriever) ;ok{
fmt.Println(retriever.Contents)
}else {
fmt.Println("类型错误")
}
一般使用type assertion
来判断接口的类型.这里我们发现其实这是一个类型转换,而且GO语言中,类型转换还会给我们返回两个值,第一个是转换的类型,第二个是否转换成功.
接口里面是什么东西?
这一小节我们来研究一下,接口里面是什么东西.
func main() {
var r Retriever = mook.Retriever{"this is a fake imook.com"}
fmt.Printf("%T %v\n",r,r)
r= real2.Retriever{
UserAgent:"Mozilla/5.0",
TimeOut:time.Minute,
}
fmt.Printf("%T %v\n",r,r)
}
打印:
mook.Retriever {this is a fake imook.com}
real.Retriever {Mozilla/5.0 1m0s}
可以看出interface 其内部有一个 interface 类型和值 (值也可以是指针)
所以,其实interface是一个数据结构,其中包含了接口的类型与实现者的值.
作者所有的学习源码在 go学习源码github地址,如果觉得有用的话帮小智贡献一个star😁