go语言的接口(接口属于对象)定义使用了duck typing这个思想。下面来看go语言的一个函数代码:
- func isDuck(duck Duck) bool {
- return duck.AmDuck()
- }
这里的isDuck函数被称为使用者,它使用了传入duck的amDuck()函数,因此duck被称为实现者。
go语言接口由使用者定义,因为使用者用到了amDuck()这个函数,因此传入的duck接口需要定义amDuck()这个方法,这种思维更符合duck typing的思维,使用者觉得传入的对象拥有我需要的功能,我就认为你是这个接口类型。
而传统的接口定义是反着来的,首先是实现者自己定义方法,使用者需要查看实现者定义了哪些方法,看自己是否能够使用。
新建一个 demo 文件夹,在 demo 文件夹下新建 go.mod
文件和 main.go
文件,再在 demo 文件夹下新建一个 yellow文件夹,在 yellow 文件夹下新建一个 SmallDuck.go
文件
go mod init demo
go语言接口定义代码:
- type Duck interface {
- AmDuck() bool
- }
go语言接口的子类实现:
- package yellow //yellow文件夹
-
- import "fmt"
-
- type SmallDuck struct {
- Foot int
- }
-
- func (smallDuck SmallDuck) AmDuck() bool {
- if smallDuck.Foot == 2 {
- return true
- }
- return false
- }
-
- func (smallDuck SmallDuck) String() string {
- return fmt.Sprintf("foot is %d", smallDuck.Foot)
- }
struct如果实现了Amduck()的话,它就会被认为是Duck的实现类,这种隐式的接口实现定义,既能检查传入的类是否实现了AmDuck方法,又能实现以接口方式作为传入参数的效果。
需要注意的是实现类的名称和属性以及方法名称都要大写,其它包才可以进行调用,否则将会出现报错
go语言接口的使用:
- package main
-
- import "demo/yellow"
-
- func isDuck(duck Duck) bool {
- return duck.AmDuck()
- }
-
- type Duck interface {
- AmDuck() bool
- }
-
- func main() {
- duck := yellow.SmallDuck{2}
- print(isDuck(duck))
- }
isDuck传入的参数被定义为一个接口,而这里传入的是我们的实现类,实现了解耦的效果
Stringer接口包含了String方法,它的作用和java的toString一样,我们在打印重写了String方法这个对象的时候,会打印出返回的string值。定义一个结构体SmallDuck,它重写了String方法。具体的调用:
- type UglyDuckling interface{}
- func main() {
- var uglyDuckling UglyDuckling
- uglyDuckling = yellow.SmallDuck{Foot: 2}
- fmt.Printf("%T %v \n", uglyDuckling, uglyDuckling)
- }
结果如下:
yellow.SmallDuck foot is 2
输出的SamllDuck的value为自定义的内容,即String方法返回的值
type Writer interface { Write(p []byte) (n int, err error)}
go语言的Writer接口定义了一个Write方法,当实现者为文件的时候,通过Write方法可以将byte数组里面内容write进入文件当中。
type Reader interface { Read(p []byte) (n int, err error)}
Reader接口定义了一个Read方法,当实现者为文件的时候,通过Read方法可以将文件里面的内容写进byte数组当中。除了文件实现了Reader接口和Writer接口外,byte数组,byte切片,网络流相关的也实现了Reader接口。因此,涉及到读写底层的东西,我们传入的是Writer和Reader接口,而不是File。例如fmt包的Fprintf方法和Fscanf方法
- func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
- p := newPrinter()
- p.doPrintf(format, a)
- n, err = w.Write(p.buf)
- p.free()
- return
- }
-
- func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error) {
- s, old := newScanState(r, false, false)
- n, err = s.doScanf(format, a)
- s.free(old)
- return
- }
具体的Reader操作例子。定义读取Reader里面内容的方法
- func printContents(reader io.Reader) {
- scanner := bufio.NewScanner(reader)
- for scanner.Scan() {
- fmt.Println(scanner.Text())
- }
- }
具体调用
- func main() {
- s := `1 12!!!`
- printContents(strings.NewReader(s))
- }
通过``定义一个跨行的字符串,通过string.NewReader()方法将s转为Reader类型,然后调用定义的方法,通过Scanner读取字符打印在控制台上。
除了Reader和Writer外,还有组合它们的接口ReadWriter
- type ReadWriter interface {
- Reader
- Writer
- }
实现者只要实现方法即可,具体的组合和调用由使用者决定。