1.接口interface
1)接口是一个或多个方法签名的集合
2)只要某个类型拥有该接口的所有方法签名,即算实现该接口无需显示声明实现了哪个接口,这称为Structural Typing
3)接口只有方法声明,没有实现,没有数据字段
4)接口可以匿名嵌入其他接口,或嵌入到结构中
5)将对象赋值给接口时,会发生拷贝,而接口内部存储的是指向这个复制品的指针,既无法修改复制品的状态,也无法获取指针
6)只有当接口存储的类型和对象为nil时,接口才等于nil
7)接口调用不会做receiver的自动转换
8)接口统一支持匿名自动方法
9)接口也可以实现类似OOP中的多态
10)空接口可以作为任何类型数据的容器
11)接口是由使用者定义得
2.类型断言
1)通过类型断言的ok pattern可以判断接口中的数据类型
2)使用tupe switch 则可针对空接口进行比较全面的类型判断
3.接口转换
可以将拥有超集的接口转换为子集的接口
实例,使用者定义接口
创建一个retiever目录,里面包含main.go 以及csdn/csdnRetiever.go两个文件
main.go代码,这里面使用到了定义的csdn这个包调用接口Retiever的Get方法:
package main
import (
"fmt"
"my-code/test-code/retiever/csdn"
)
type Retiever interface {
Get(url string) string
}
func download(r Retiever) string {
return r.Get("www.csdn.com")
}
func main() {
var r Retiever
r = csdn.Retiever{"hello www.csdn.com"}
fmt.Println(download(r))
}
csdn/csdnRetiever.go代码中包含接口定义:
package csdn
type Retiever struct {
Contents string
}
func (r Retiever) Get(url string) string {
return r.Contents
}
上述代码传入一个url字符串并直接return
输出:
API server listening at: 127.0.0.1:34276
hello www.csdn.com
实例,访问百度
创建一个retiever目录,里面包含main.go 以及baidu/baidu.go两个文件
main.go代码,这里面使用到了定义的baidu这个包调用接口Retiever的Get方法:
package main
import (
"fmt"
"my-code/test-code/retiever/baidu"
)
type Retiever interface {
Get(url string) string
}
func download(r Retiever) string {
return r.Get("http://www.baidu.com")
}
func main() {
var r Retiever
r = baidu.Retiever{}
fmt.Println(download(r))
}
baidu/baidu.go代码直接调用内置http接口访问百度:
package baidu
import (
"net/http"
"net/http/httputil"
"time"
)
type Retiever struct {
UserAgent string
TimeOut time.Duration
}
func (r Retiever) Get(url string) string {
resp, err := http.Get(url)
if err != nil {
panic(err)
}
result, err := httputil.DumpResponse(resp, true)
resp.Body.Close()
if err != nil {
panic(err)
}
return string(result)
}
上述实例运行结果是获取到了百度首页的html源码
4.查看接口内部元素
接口内部究竟是什么内容呢,将上述main.go代码修改如下打印出来查看:
func main() {
var r Retiever
r = baidu.Retiever{}
//fmt.Println(download(r))
fmt.Printf("%T\n%v\n", r, r)
}
运行结果如下:
API server listening at: 127.0.0.1:18265
baidu.Retiever
{ 0s}
上述打印看出%T打印的内容是baidu.Retiever这是接口名称,%v的内容是{ 0s},这是因为里面两个元素UserAgent未赋值因此是个空格,TimeOut未赋值所以默认0秒。为了更清晰的看出效果,下面将上述元素赋值查看:
package main
import (
"fmt"
"my-code/retiever/baidu"
"time"
)
type Retiever interface {
Get(url string) string
}
func download(r Retiever) string {
return r.Get("http://www.baidu.com")
}
func main() {
var r Retiever
r = baidu.Retiever{
UserAgent: "Mozilla/5.0",
TimeOut: time.Minute,
}
//fmt.Println(download(r))
fmt.Printf("%T\n%v\n", r, r)
}
运行效果如下:
API server listening at: 127.0.0.1:16843
baidu.Retiever
{Mozilla/5.0 1m0s}
可以明显看出被赋值了
5.指针类型
如果接口类型修改为指针类型呢,代码如下:
baidu/baidu.go
package baidu
import (
"net/http"
"net/http/httputil"
"time"
)
type Retiever struct {
UserAgent string
TimeOut time.Duration
}
func (r *Retiever) Get(url string) string {
resp, err := http.Get(url)
if err != nil {
panic(err)
}
result, err := httputil.DumpResponse(resp, true)
resp.Body.Close()
if err != nil {
panic(err)
}
return string(result)
}
main.go源码不变:
package main
import (
"fmt"
"my-code/retiever/baidu"
"time"
)
type Retiever interface {
Get(url string) string
}
func download(r Retiever) string {
return r.Get("http://www.baidu.com")
}
func main() {
var r Retiever
r = baidu.Retiever{
UserAgent: "Mozilla/5.0",
TimeOut: time.Minute,
}
//fmt.Println(download(r))
fmt.Printf("%T\n%v\n", r, r)
}
那么上述就会出错,执行报错如下:
# my-code/retiever
.\main.go:19:4: cannot use baidu.Retiever literal (type baidu.Retiever) as type Retiever in assignment:
baidu.Retiever does not implement Retiever (Get method has pointer receiver)
exit status 2
Process exiting with code: 1
此时就要修改为取地址了:
主函数修改如下:
func main() {
var r Retiever
r = &baidu.Retiever{
UserAgent: "Mozilla/5.0",
TimeOut: time.Minute,
}
//fmt.Println(download(r))
fmt.Printf("%T\n%v\n", r, r)
}
这样执行就不会出错了。
Type assertion
通过指针类型获取内部元素:
val := r.(*baidu.Retiever)
fmt.Println(val.TimeOut)
这样就可以取得具体元素了。
6.接口的组合
baidu/baidu.go提供Retiever接口:
package baidu
type Retiever struct {
Contents string
}
func (r *Retiever) Post(url string, form map[string]string) string {
r.Contents = form["contents"]
return "ok"
}
func (r *Retiever) Get(url string) string {
return r.Contents
}
main.go代码这里面使用Poster和Retiever两个接口组合成一个RetieverPoster接口
package main
import (
"fmt"
"my-code/retiever/baidu"
)
type Retiever interface {
Get(url string) string
}
type Poster interface {
Post(url string, form map[string]string) string
}
const url = "http://www.baidu.com"
func download(r Retiever) string {
return r.Get(url)
}
func post(poster Poster) {
poster.Post(url,
map[string]string{
"name": "baidu",
"course": "golang",
})
}
type RetieverPoster interface {
Retiever
Poster
}
func session(s RetieverPoster) string {
s.Post(url, map[string]string{
"contents": "hello baidu",
})
return s.Get(url)
}
func main() {
retriever := baidu.Retiever{"hello http://www.baidu.com"}
fmt.Println(session(&retriever))
}
执行效果如下:
API server listening at: 127.0.0.1:41871
hello baidu
虽然传入的是"hello http://www.baidu.com"但是输出的却是"hello baidu"这是因为在session方法里面修改了内容导致的。
7.GO语言常用系统接口
Stringer接口,在fmt/print.go内:
type Stringer interface {
String() string
}
应用:在baidu包内实现一个String(),代码如下:
baidu/baidu.go
package baidu
import "fmt"
type Retiever struct {
Contents string
}
func (r *Retiever) String() string {
return fmt.Sprintf(
"Retriver:{Contents=%s}", r.Contents)
}
func (r *Retiever) Post(url string, form map[string]string) string {
r.Contents = form["contents"]
return "ok"
}
func (r *Retiever) Get(url string) string {
return r.Contents
}
main.go
package main
import (
"fmt"
"my-code/retiever/baidu"
)
type Retiever interface {
Get(url string) string
}
func main() {
r := &baidu.Retiever{
Contents: "hello word",
}
fmt.Printf("%T\n%v\n", r, r)
}
输出:
API server listening at: 127.0.0.1:39863
*baidu.Retiever
Retriver:{Contents=hello word}
此外还有两个接口在io.go包内,分别是Reader和Writer
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
实例:
package main
import (
"bufio"
"fmt"
"io"
"os"
"strings"
)
func printFile(name string) {
file, err := os.Open(name)
if err != nil {
panic(err)
}
testIoReader(file)
}
func testIoReader(reader io.Reader) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
func main() {
printFile("ioTest.go")
fmt.Println("##############print string#################")
s := `abc"d"
beijing
nihao
hello`
testIoReader(strings.NewReader(s))
}
可以打印文件内容或者字符串里面的内容。
小结:
1.接口变量里面有什么
1)接口变量自带指针
2)接口变量同样采用值传递,几乎不需要使用接口的指针
3)指针接收者实现只能以指针方式使用;值接受者都可
2.查看接口变量
1)表示任何类型:interface{}
2)Type Assertion
3)Type Switch