内容简介:golang是面向接口的编程语言,这是golang的语言特点之一。golang传统意义上的继承,多态等,它只支持封装。golang中的接口并不是传统(c/c++,java等)意义上的接口,而是传统面向对象中继承和多态在golang中是通过接口来完成的。在golang的interface出现之前,接口主要作为不同组建之间的契约存在的,对契约的实现是强制的,必须明确声明实现了这个接口。但是golang完全不同。多态是指代码可以根据类型的具体实现采用不同行为的能力,接口是一个或多个方法签名的集合,只要某个类型拥
接口interface
golang是面向接口的编程语言,这是golang的语言特点之一。golang传统意义上的继承,多态等,它只支持封装。golang中的接口并不是传统(c/c++,java等)意义上的接口,而是传统面向对象中继承和多态在golang中是通过接口来完成的。在golang的interface出现之前,接口主要作为不同组建之间的契约存在的,对契约的实现是强制的,必须明确声明实现了这个接口。但是golang完全不同。多态是指代码可以根据类型的具体实现采用不同行为的能力,接口是一个或多个方法签名的集合,只要某个类型拥有该接口的所有方法签名,即算实现该接口,无需显示声明实现了哪个接口,这称为StructuralTyping,接口只有方法声明,没有实现,没有数据字段接口可以匿名嵌入其它接口,或嵌入到结构中,将对象赋值给接口时,会发生拷贝,而接口内部存储的是指向这个复制品的指针,既无法修改复制品的状态,也无法获取指针,只有当接口存储的类型和对象都为nil时,接口才等于nil,接口调用不会做receiver的自动转换,接口同样支持匿名字段方法,接口也可实现类似OOP中的多态,空接口可以作为任何类型数据的容器
eg:
type Traversal interface{ Traverse() } func main(){ traversal := getTraversal() traversal.Traverse() }
通过getTraversal()方法封装得到了一个traversal,这个traversal实现了Traversal这个interface
duck typing
许多语言支持duck typing,包括c++,python, java无法实现duck typing 。
duck typing的概念:像鸭子走路,像鸭子叫,长的像鸭子,那么它就是一只鸭子
duck typing的用途:描述事物的外部行为而非内部结构
在golang编程中就利用duck typing的方式进行,gopher不用在意调用方法的“类型”是一个什么,当gopher需要调用到某个方法时能提供这个方法即可,然后和这些方法进行互动
长得像鸭子就是鸭子,也就是说实现了interface就是实现了interface中定义的那个方法,那么实现该方法的struct就不用赋值给interface就拥有了这个能力。也就是说实现了这个struct的实例可以被fmt.Println调用。但是在定义的时候其实是interface实例被调用。只不过这个struct实现了这个接口,所以它也拥有了这个能力。
严格说来 go 属于结构化类型系统,类似duck typing,因为在定义duck typing时强调了“动态绑定”,但是golang是编译时就绑定了,从这个角度看,golang就不是duck typing
golang接口的定义
graph LR 使用者-->实现者
golang中,一个类只需要实现接口要求的所有函数,我们就说这个类实现了这个接口
golang的非侵入式接口,看似做了很小的语法调整,实则影响很是深远:
- go标准库,再也不需要绘制类库继承树图;
- 实现类的时候,只需要关心自己应该实现哪些方法,不必纠结接口要拆得多细才合理,接口由使用方法按需定义,而不用事前规划;
- 不用为了实现一个接口而导入一个包,因为多引用一个外部包,就意味着更多的耦合,接口使用方按自身需求定义,使用方无需关心是否有其他模块定义过类似的接口。
代码示例,自主实现一个下载https://studygolang.com/网页的代码(其中含有多态的概念):
main()
package main import ( "fmt" "interfaceType/mock" real2 "interfaceType/real" ) type Link interface { Get(url string) string } func download(l Link)string { return l.Get("https://studygolang.com/") } func main() { var l,m Link l = mock.Retriever{"This is a fack link"} fmt.Println(download(l)) m = new(mock.Retriever) fmt.Println(download(m)) var r Link r = real2.Retriever{} fmt.Println(download(r)) }
mockRetriever
package mock type Retriever struct { Contents string } func (r Retriever) Get(url string) string { if r.Contents == ""{ return url } return r.Contents }
realRetriever
package real import ( "time" "net/http" "net/http/httputil" ) type Retriever struct { UserAgent string TimeOut time.Duration } func (r Retriever) Get(url string) string { response, err := http.Get(url) if err != nil { panic(err) } result, err := httputil.DumpResponse(response, true) if err != nil { panic(err) } defer response.Body.Close() return string(result) }
利用标准库实现下载一个下载https://studygolang.com/网页的代码:
package main import ( "fmt" "io" "net/http" "os" ) // main is the entry point for the application. func main() { // Get a response from the web server. r, err := http.Get("https://studygolang.com/") if err != nil { fmt.Println(err) return } // Copies from the Body to Stdout. io.Copy(os.Stdout, r.Body) if err := r.Body.Close(); err != nil { fmt.Println(err) } }
观察以上代码,不难发现,使用标准库实现网页的download更容易一些当然,我们这里要研究的东西是golang的接口,download的实现是次要的。
r, err := http.Get("https://studygolang.com/")
利用http.Get()函数得到一个http.Response类型的指针,观察一下http.Get()函数源码:
func Get(url string) (resp *Response, err error) { return DefaultClient.Get(url) }
在观察一下http.Response:
type Response struct { Status string StatusCode int Proto string ProtoMajor int ProtoMinor int Header Header Body io.ReadCloser ContentLength int64 TransferEncoding []string Close bool Uncompressed bool Trailer Header Request *Request TLS *tls.ConnectionState }
原来http.Response是一个结构,不难发现里面的Body是一个io.ReadCloser类型的,让我们再去看看io.ReadCloser的源码:
type ReadCloser interface { Reader Closer }
原来Body是一个接口组合,实现了Reader和Closer方法
接下来继续看
io.Copy(os.Stdout, r.Body)
io.Copy()函数的源码:
func Copy(dst Writer, src Reader) (written int64, err error) { return copyBuffer(dst, src, nil) }
第一个参数是一个Writer,第二个参数是一个Reader,当然,我们的Body实现了Reader这个接口,所以可以直接作为参数传入。
接口赋值
golang接口的赋值有以下两种:
- 将对象实例赋值给接口:要求该对象实例实现了接口要求的所有方法
- 将一个接口赋值给另外一个接口:
将实例化对象赋值给接口
package main import "fmt" type myInt int type LessAdd interface { Less(b myInt) bool Add(b myInt) } func main() { var a myInt = 2 var b LessAdd = &a b.Add(a) fmt.Println(b.Less(0)) fmt.Printf("%d\n",a) } func (a myInt) Less(b myInt) bool { return a < b } func (a *myInt) Add(b myInt) { *a += b }
有一个有趣的问题:a赋值给接口LessAdd时候,用的是地址的引用,而不是值的引用
var a myInt = 2 var b LessAdd = &a
接口LessAdd中包含了一个指针的方法func (a *myInt) Add(b myInt),在调用非指针方法func (a myInt) Less(b myInt) bool时候,golang会自动生成一个函数func(a *myInt)Less(b myInt)bool,这就解决了指针传入的问题
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
硅谷百年史
[美]阿伦·拉奥(Arun Rao)、[美]皮埃罗·斯加鲁菲(Piero Scarruffi) / 闫景立、侯爱华 / 人民邮电出版社 / 2014-4-1 / 99.00
一百多年来,仅硅谷就培育了50多位诺贝尔奖获得者,以及无数依靠智慧和知识而成为百万富翁的人。这一人类历史上最伟大的科技创新与创业历程为什么会发生在硅谷?究竟是如何发生的?其他地方是否可以复制出“硅谷”? 《硅谷百年史——伟大的科技创新与创业历程(1900-2013)》以编年体的顺序,从无线电技术、晶体管、集成电路,到人类基因组、互联网和云计算,详尽地记述了硅谷在100多年中所发生的重大科技事......一起来看看 《硅谷百年史》 这本书的介绍吧!