go接口

简介: go接口

接口类型是对其它类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式我们可以让我们的函数更加灵活和更具有适应能力。

Go语言中接口类型的独特之处在于它是满足隐式实现的。也就是说,如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口。因此,我们可以通过将接口作为参数来实现对不同类型的调用,从而实现多态。

接口约定

当你有看到一个接口类型的值时,你不知道它是什么,唯一知道的就是可以通过它的方法来做什么。 接口可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。

go

复制代码

type ByteCounter int
func (c *ByteCounter) Write(p []byte) (int, error) {
  *c += ByteCounter(len(p)) // convert int to ByteCounter
  return len(p), nil
}
func main() {
  var c ByteCounter
  c.Write([]byte("hello"))
  fmt.Println(c) // "5", = len("hello")
  c = 0          // reset the counter
  var name = "Dolly"
  fmt.Fprintf(&c, "hello, %s", name)
  fmt.Println(c) // "12", = len("hello, Dolly")
}

![[Pasted image 20231127204316.png]] Fprintf 函数接受一个 io.Writer 的接口类型 要让我们的 ByteCounter 类型可以被 Fprintf 函数接受,就需要我们实现 Fprintf 函数的方法(唯一的一个 Write 方法)

接口类型

接口类型具体描述了一系列方法的集合,一个实现了这些方法的具体类型是这个接口类型的实例。

go

复制代码

import "fmt"
type person interface {
  Eat() (msg string)
  Touch() (msg string)
}
type user struct{}
func (p *user) Eat() (msg string) {
  return "full"
}
func (p *user) Touch() (msg string) {
  return "hurt"
}
func main() {
  var cfd user
  fmt.Println(cfd.Eat())   //"full"
  fmt.Println(cfd.Touch()) //"hurt"
}

实现接口的条件

一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口。接口指定的规则非常简单:表达一个类型属于某个接口只要这个类型实现这个接口。所以:

go

复制代码

var w io.Writer         // 只用实现 Write 方法
w = os.Stdout           // OK: *os.File 有 Write 方法
w = new(bytes.Buffer)   // OK: *bytes.Buffer 有 Write 方法
w = time.Second         // compile error: time.Duration 没有 Write 方法
var rwc io.ReadWriteCloser // 需要实现 Read, Write, Close 方法
rwc = os.Stdout         // OK: *os.File 有 Read, Write, Close 方法
rwc = new(bytes.Buffer) // compile error: *bytes.Buffer 没有 Close 方法

这个规则甚至适用于等式右边本身也是一个接口类型

go

复制代码

w = rwc                 // OK: io.ReadWriteCloser 有 Write 方法
rwc = w                 // compile error: io.Writer 没有 Close 方法

go

复制代码

type IntSet struct {
}
func (*IntSet) String() string {
  return ""
}
var _ = IntSet{}.String()    // compile error: String 需要 *IntSet 接收
var _ = (&IntSet{}).String() // OK: s 是一个变量而 &s 有 a String 方法

go

复制代码

var s IntSet
var _ fmt.Stringer = &s // OK
var _ fmt.Stringer = s  // compile error: IntSet 没有 String 方法

由于只有 *IntSet 类型有 String 方法,所以也只有 *IntSet 类型实现了 fmt.Stringer 接口:

空接口

go

复制代码

var any interface{}
any = true
any = 12.34
any = "hello"
any = map[string]int{"one": 1}
any = new(bytes.Buffer)

interface{}被称为空接口类型是不可或缺的。 因为空接口类型对实现它的类型没有要求,所以我们可以将任意一个值赋给空接口类型。

接口值

接口值,由两个部分组成,一个具体的类型那个类型的值。它们被称为接口的动态类型动态值

接口值 · Go语言圣经 (studygolang.com)

一个包含nil指针的接口不是nil接口

一个不包含任何值的nil接口值和一个刚好包含nil指针的接口值是不同的。这个细微区别产生了一个容易绊倒每个Go程序员的陷阱。

go

复制代码

package main
import (
  "bytes"
  "fmt"
  "io"
)
const debug = false
func main() {
  var buf *bytes.Buffer = nil
  if debug {
    buf = new(bytes.Buffer) // enable collection of output
  }
  //buf.Write([]byte("hello"))
  f(buf) // NOTE: subtly incorrect!
  if debug {
    // ...use buf...
  }
}
// If out is non-nil, output will be written to it.
func f(out io.Writer) {
  fmt.Printf("%T", out)
  // ...do something...
  if out != nil {
    out.Write([]byte("done!\n"))
  }
}

main函数调用函数f时,它给f函数的out参数赋了一个*bytes.Buffer的空指针,所以out的动态值是nil。然而,它的动态类型是*bytes.Buffer,意思就是out变量是一个包含空指针值的非空接口,所以防御性检查out!=nil的结果依然是true

问题在于尽管一个nil的 *bytes.Buffer 指针有实现这个接口的方法,它也不满足这个接口具体的行为上的要求。

解决方案就是避免一开始就将一个不完整的值赋值给这个接口:

go

复制代码

var buf io.Writer
if debug {
    buf = new(bytes.Buffer) // enable collection of output
}
f(buf) // OK


相关文章
|
6月前
|
自然语言处理 Go 索引
Go语言学习8-接口类型
【4月更文挑战第1天】本篇 Huazie 向大家介绍 Go 语言中的接口类型
43 2
Go语言学习8-接口类型
|
6月前
|
设计模式 存储 监控
《Go 简易速速上手小册》第4章:接口与抽象(2024 最新版)(上)
《Go 简易速速上手小册》第4章:接口与抽象(2024 最新版)
69 1
|
6月前
|
Go
|
14天前
|
存储 Rust Go
Go nil 空结构体 空接口有什么区别?
本文介绍了Go语言中的`nil`、空结构体和空接口的区别。`nil`是预定义的零值变量,适用于指针、管道等类型;空结构体大小为0,多个空结构体实例指向同一地址;空接口由`_type`和`data`字段组成,仅当两者均为`nil`时,空接口才为`nil`。
Go nil 空结构体 空接口有什么区别?
|
5月前
|
Go 数据安全/隐私保护
go 基于gin编写encode、decode、base64加密接口
go 基于gin编写encode、decode、base64加密接口
48 2
|
2月前
|
存储 Go
Go to Learn Go之接口
Go to Learn Go之接口
31 7
|
3月前
|
存储 缓存 NoSQL
在 Go 中使用接口进行灵活缓存
在 Go 中使用接口进行灵活缓存
|
3月前
|
XML 存储 JSON
在Go中使用接口:实用性与脆弱性的平衡
在Go中使用接口:实用性与脆弱性的平衡
|
3月前
|
SQL 安全 测试技术
[go 面试] 接口测试的方法与技巧
[go 面试] 接口测试的方法与技巧
|
6月前
|
存储 安全 Go
掌握Go语言:Go语言类型转换,无缝处理数据类型、接口和自定义类型的转换细节解析(29)
掌握Go语言:Go语言类型转换,无缝处理数据类型、接口和自定义类型的转换细节解析(29)
134 0