Go 学习笔记(七)- 接口

综合编程 2016-04-25

接口类型是对其它类型行为的抽象和概括,我的理解是,可以更方便的处理不同类型数据。在 js 里没有特别的明确数据类型,比如 1 + '2'
'12'
1 - '2'
-1
,在 go 里这样的语句是错误的。

接口的概念非常多,小章节是之前每个章节的2,3倍。

接口约定

由于 go 是强类型的静态语言,例如一个函数定义时声明了类型,则只能处理该类数据。如果仅仅是数据类型不同,处理方式一模一样的情况,难道要重写一份吗?肯定不是这样的。

接口类型,将改变着一切。

最简单的例子,如 fmt.Printf
可以接受任何类型的数据。

package fmt

func Fprintf(w io.Writer, format string, args ...interface{}) (int, error)
func Printf(format string, args ...interface{}) (int, error) {
return Fprintf(os.Stdout, format, args...)
}
func Sprintf(format string, args ...interface{}) string {
var buf bytes.Buffer
Fprintf(&buf, format, args...)
return buf.String()
}

虽然一开始我没看懂,但是看完后才发现原来如此简单。

参数部分是 args ...interface{}
,是一个空接口,可以理解为接收任意类型的参数。

其中 io.Writer
是‘可写’接口:

package io

type Writer interface {
Write(p []byte) (n int, err error)
}

这么看感觉只是把函数声明写到这里而已,确实是这样的,把函数声明写一遍即可。

这个接口的意思为:某个类型只要有 Write
方法,就可以接收。

type ByteCounter int

func (c *ByteCounter) Write(p []byte) (int, error) {
*c += ByteCounter(len(p)) // convert int to ByteCounter
return len(p), nil
}

比如这个 int
的别名 ByteCounter
类型,定义了一个 Write
方法。

var c ByteCounter
var name = "world"
fmt.Fprintf(&c, "hello, %s", name)

fmt.Println(c) // 12

他就可以当做第一个参数被调用,而且正确执行了 Write
方法。

第一小节花的时间略多,因为概念基本在这了,后面只是延伸和应用。

接口类型

接口类型其实跟结构体类似,只是关键词不一样,因为接口也可以像结构体一样声明跟嵌入,这里就不多解释了。

实现接口的条件

一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口。

简单说就是,如果一个接口有 a,b 方法,你的类型必须也有 a,b 方法才能满足这个接口类型,否则无法被赋值或当参数接收。

但如果你的类型有 a,b,c,d 甚至更多方法,那么你的类型也满足了这个接口。

简单说就是只能多不能少,只有被满足的才能被赋值或当参数接收,当然空接口除外,任何数据都满足空接口。

flag.Value 接口

粗略看了,貌似处理命令行输入的,通过 flag.Value
接口,自定义处理控制台输入。

type Value interface {
String() string
Set(string) error
}

接口值

概念上讲一个接口的值,接口值,由两个部分组成,一个具体的类型和那个类型的值。

var w io.Writer
// 它的类型和类型的值,描述如下:
w
┌─────────┐
typenil
├─────────┤
value │ nil
└─────────┘

w = os.Stdout
// 赋值后如下:
w
┌──────────────┐
type │ *os.File │ os.File
├──────────────┤ ┌───────────────────┐
value │ ●───────┼──────➞│ fd int = 1(stdot) │
└──────────────┘ └───────────────────┘

这货说难也难,说简单也简单。

sort.Interface 接口

排序接口,可以对任意数据类型进行排序,只要简单实现3个接口即可。

package sort

type Interface interface {
Len() int
Less(i, j int) bool // i, j 是序列中的元素索引(Less类似js数组的sort回调函数,只是这里是索引)
Swap(i, j int)
}

例子就略了。。

http.Handler 接口

略,等学 web 的时候细学。

error 接口

简单粗暴:

type error interface {
Error() string
}

创建一个error最简单的方法就是调用errors.New函数,它会根据传入的错误信息返回一个新的error。整个errors包仅只有4行:

package errors

func New(text string) error { return &errorString{text} }

type errorString struct { text string }

func (e *errorString) Error() string { return e.text }

两个 error 是不相等的。

fmt.Println(errors.New("EOF") == errors.New("EOF")) // "false"

类型断言

使用 x.(T)
语法对 x 进行 T 类型的断言。

var w io.Writer
w = os.Stdout
f := w.(*os.File) // success: f == os.Stdout
c := w.(*bytes.Buffer) // panic: interface holds *os.File, not *bytes.Buffer

当类型断言的操作对象是一个变量,你有时会看见原来的变量名重用而不是声明一个新的本地变量,这个重用的变量会覆盖原来的值,如下面这样:

if w, ok := w.(*os.File); ok {
// ...use w...
}

小结

后续几个小章结都是应用,等实际应用时踩坑了,光这样死记硬背也没用。

楼教主

责编内容by:楼教主 (源链)。感谢您的支持!

您可能感兴趣的

Go中实现手动内存分配的坑 2016-07-10 你一定想到过,分配一块大的内存,然后从里面切小的对象出来,手动管理对象分配。分配的开销非常小,就是offset加一下。尤其是有些场景,...
Go语言实战笔记(十六)| Go 并发示例-Pool... 《Go语言实战》读书笔记,未完待续,欢迎扫码关注公众号 flysnow_org 或者网站 http://www.flysnow.org/ ,第一时间看后续笔记...
TB一周萃选 我看过小马哥(哈维尔·马斯切拉诺)踢球, 你看过小马哥踢球, 他看过小马哥踢球。 我们看过小马哥踢球, ...
通往 Go 2 之路 Go 语言官方博客发表文章谈论了 未来的 Go 2 计划 ,征询社区和用户的意见。官方博客称,Go 语言的酝酿到发布经历了五年,Go 1.x...
不得不知道的Golang之sync.Map源码分析 众所周知,go普通的map是不支持并发的,换而言之,不是线程(goroutine)安全的。博主是从golang 1.4开始使用的,那时候map的并发读是没...