GO-接口

1. 接口

在Go语言中接口(interface)是一种类型,一种抽象的类型。

interface是一组method的集合,接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法)。

接口(interface)是一种类型

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

接口是双方约定的一种合作协议。接口实现者不需要关心接口会被怎样使用,调用者也不需要关心接口的实现细节。接口是一种类型,也是一种抽象结构,不会暴露所含数据的格式、类型及结构。

1.2 接口定义

Go语言提倡面向接口编程。

每个接口类型由数个方法组成。接口的形式代码如下:

type 接口类型名 interface{方法名1( 参数列表1 ) 返回值列表1方法名2( 参数列表2 ) 返回值列表2…
}

对各个部分的说明:

  • 接口类型名:使用 type 将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加 er,如有写操作的接口叫 Writer,有字符串功能的接口叫 Stringer,有关闭功能的接口叫 Closer 等。
  • 方法名:当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
  • 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以被忽略
type Writer interface {//接口名和方法首字母大写,意味着可以被其他包访问Write([]byte)string
}

1.3 接口实现条件

如果一个任意类型 T 的方法集为一个接口类型的方法集的超集,则我们说类型 T 实现了此接口类型。

T 可以是一个非接口类型,也可以是一个接口类型。

实现关系在Go语言中是隐式的。两个类型之间的实现关系不需要在代码中显式地表示出来。Go语言中没有类似于 implements 的关键字。 Go编译器将自动在需要的时候检查两个类型之间的实现关系。

接口定义后,需要实现接口,调用方才能正确编译通过并使用接口。

接口的实现需要遵循两条规则才能让接口可用:

  1. 接口的方法与实现接口的类型方法格式一致在类型中添加与接口签名一致的方法就可以实现该方法。签名包括方法中的名称、参数列表、返回参数列表。也就是说,只要实现接口类型中的方法的名称、参数列表、返回参数列表中的任意一项与接口要实现的方法不一致,那么接口的这个方法就不会被实现。
// 定义一个数据写入器
type DataWriter interface {Write(interface{}) error
}// 定义文件结构,用于实现DataWriter
type file struct {
}// 实现DataWriter接口的WriteData方法
func (f *file) Write(b interface{}) error {return fmt.Sprintf("writer:", b)
}func main() {// 实例化filef := new(file)// 声明一个DataWriter的接口var write DataWriter// 将接口赋值f,也就是*file类型write = f// 使用DataWriter接口进行数据写入write.Write("hhhhhhhh")
}
  1. 当类型无法实现接口时,编译器会报错:
    1. 函数名不一致导致的报错
    2. 实现接口的方法签名不一致导致的报错
  1. 接口中所有方法均被实现当一个接口中有多个方法时,只有这些方法都被实现了,接口才能被正确编译并使用。

// 定义一个数据写入器
type DataWriter interface {Write(interface{}) error//上述代码中新增一个方法Content() bool
}

运行结果

.\main.go:28:10: cannot use f (variable of type *file) as DataWriter value in assignment: *file does not implement DataWriter (missing method Content)

Go语言的接口实现是隐式的,无须让实现接口的类型写出实现了哪些接口。

这个设计被称为非侵入式设计。

1.4 类型与接口的关系

在Go语言中类型和接口之间有一对多和多对一的关系

一个类型可以实现多个接口

一个类型可以同时实现多个接口,而接口间彼此独立,不知道对方的实现。

例如,狗可以叫,也可以动。

我们就分别定义Sayer接口和Mover接口,如下:

type Sayer interface {Say()
}type Mover interface {Move()
}type Dog struct {name string
}// dog实现say和move接口
func (d Dog) Say() {fmt.Println(d.name, " saying......")
}func (d Dog) Move() {fmt.Println(d.name, "moving ......")
}func main() {var x Sayervar y Movervar dog = Dog{"wangwang"}x = dogy = dogx.Say()  //wangwang  saying......y.Move() //wangwang moving ......}

多个类型实现同一接口

type Mover interface {Move()
}type Dog struct {name string
}type Car struct {name string
}//dog 和 car都实现mover接口func (d Dog) Move() {fmt.Println(d.name, "moving,....")
}func (c Car) Move() {fmt.Println(c.name, "moving .....")
}func main() {var d = Dog{"旺财"}var c = Car{"小米"}var move Movermove = dmove.Move() //旺财 moving,....move = cmove.Move() //小米 moving .....}

接口嵌套

接口与接口间可以通过嵌套创造出新的接口

// Sayer 接口
type Sayer interface {say()
}// Mover 接口
type Mover interface {move()
}// 接口嵌套
type animal interface {SayerMover
}

嵌套得到的接口的使用与普通接口一样,这里我们让cat实现animal接口:

type cat struct {name string
}func (c cat) say() {fmt.Println("喵喵喵")
}func (c cat) move() {fmt.Println("猫会动")
}func main() {var x animalx = cat{name: "花花"}x.move()x.say()
}

1.5 空接口

空接口是指没有定义任何方法的接口。

因此任何类型都实现了空接口。

空接口类型的变量可以存储任意类型的变量。

func main() {var x interface{}var i = 100x = ifmt.Println(x) //100var name = "hhhhh"x = namefmt.Println(x)  //hhhhh
}

1.5.1 空接口的应用

空接口作为函数的参数

使用空接口实现可以接收任意类型的函数参数。

func show(a interface{}) {fmt.Println(a)
}func main() {//空接口作为函数参数show("空接口传参")  //空接口传参}

空接口作为map的值

使用空接口实现可以保存任意值的字典。

func main() {var student = make(map[string]interface{}, 3)student["小明"] = 100student["小红"] = "hahah"student["小高"] = falsefmt.Printf("%+v", student)  //map[小明:100 小红:hahah 小高:false]}

1.5.2 类型断言

空接口可以存储任意类型的值,那我们如何获取其存储的具体数据呢?

接口值

一个接口的值(简称接口值)是由一个具体类型和具体类型的值两部分组成的。

这两部分分别称为接口的动态类型和动态值。

想要判断空接口中的值这个时候就可以使用类型断言,其语法格式:

x.(T)

1

其中:

  1. x:表示类型为interface{}的变量
  2. T:表示断言x可能是的类型。

该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败。

func main() {var student = make(map[string]interface{}, 3)student["小明"] = 100student["小红"] = "hahah"student["小高"] = falsefmt.Printf("%+v\n", student) //map[小明:100 小红:hahah 小高:false]_, ok := student["小明"].(bool)if ok != true {fmt.Println("student[\"小明\"]不是bool")  }
}

2. I/O操作

I/O操作也叫输入输出操作。其中I是指Input,O是指Output,用于读或者写数据的,有些语言中也叫流操作,是指数据通信的通道。

Golang 标准库对 IO 的抽象非常精巧,各个组件可以随意组合,可以作为接口设计的典范。

io包中提供I/O原始操作的一系列接口。

它主要包装了一些已有的实现,如 os 包中的那些,并将这些抽象成为实用性的功能和一些其他相关的接口。

由于这些接口和原始的操作以不同的实现包装了低级操作,客户不应假定它们对于并行执行是安全的。

io库比较常用的接口有三个,分别是Reader,Writer和Closer。

2.1 Reader

Reader接口的定义,Read()方法用于读取数据。

type Reader interface {Read(p []byte) (n int, err error)
}

io.Reader 表示一个读取器,它将数据从某个资源读取到传输缓冲区。在缓冲区中,数据可以被流式传输和使用。

  • 对于要用作读取器的类型,它必须实现 io.Reader 接口的唯一一个方法 Read(p []byte)。
  • 换句话说,只要实现了 Read(p []byte) ,那它就是一个读取器。
  • Read() 方法有两个返回值,一个是读取到的字节数,一个是发生错误时的错误。

通过 string.NewReader(string) 创建一个字符串读取器,然后流式地按字节读取:


func main() {reader := strings.NewReader("this is a reader")// 每次读取4个字节p := make([]byte, 4)for {n, err := reader.Read(p)if err != nil {if err == io.EOF {log.Println("读完了")break}log.Fatalln("read error", err)os.Exit(2)}log.Println("读取到的字节数:", n)}}
  • 最后一次返回的 n 值有可能小于缓冲区大小。
  • io.EOF 来表示输入流已经读取到头

2.1.1 文件操作相关API
func Create(name string) (file *File, err Error)

1

    • 根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666
func NewFile(fd uintptr, name string) *File

1

    • 根据文件描述符创建相应的文件,返回一个文件对象
func Open(name string) (file *File, err Error)

1

    • 只读方式打开一个名称为name的文件
func OpenFile(name string, flag int, perm uint32) (file *File, err Error)

1

    • 打开名称为name的文件,flag是打开的方式,只读、读写等,perm是权限
func (file *File) Write(b []byte) (n int, err Error)

1

    • 写入byte类型的信息到文件
func (file *File) WriteAt(b []byte, off int64) (n int, err Error)

1

    • 在指定位置开始写入byte类型的信息
func (file *File) WriteString(s string) (ret int, err Error)

1

    • 写入string信息到文件
func (file *File) Read(b []byte) (n int, err Error)

1

    • 读取数据到b中
func (file *File) ReadAt(b []byte, off int64) (n int, err Error)

1

    • 从off开始读取数据到b中
func Remove(name string) Error

1

    • 删除文件名为name的文件
2.1.2 读文件

type Closer interface {Close() error
}

os.Open()函数能够打开一个文件,返回一个*File和一个err。对得到的文件实例调用Close()方法能够关闭文件。

文件读取可以用file.Read(),读到文件末尾会返回io.EOF的错误

func main() {// 打开文件file, err := os.Open("C:\\Users\\Administrator\\Desktop\\新建 文本文档 (2).txt")if err != nil {log.Println("打开失败")}defer file.Close()// 定义接收文件读取的字节数组buff := make([]byte, 128)var content []bytefor {_, err := file.Read(buff)if err == io.EOF {log.Println("读完了")break}if err != nil {log.Println("读取失败:", err)return}}content = append(content, buff...)fmt.Sprintln(content)
}

Writer

type Writer interface {//Write() 方法有两个返回值,一个是写入到目标资源的字节数,一个是发生错误时的错误。Write(p []byte) (n int, err error)
}

  • io.Writer 表示一个写入器,它从缓冲区读取数据,并将数据写入目标资源。
  • 对于要用作编写器的类型,必须实现 io.Writer 接口的唯一一个方法 Write(p []byte)
  • 同样,只要实现了 Write(p []byte) ,那它就是一个编写器。
func main() {// 打开文件file, err := os.Open("C:\\Users\\Administrator\\Desktop\\新建 文本文档 (2).txt")if err != nil {log.Println("打开失败")}defer file.Close()// 定义接收文件读取的字节数组buff := make([]byte, 128)var content []bytefor {_, err := file.Read(buff[:])if err == io.EOF {log.Println("读完了")break}if err != nil {log.Println("读取失败:", err)return}}content = append(content, buff...)fmt.Println(string(content))
}

2.2 Writer

type Writer interface {//Write() 方法有两个返回值,一个是写入到目标资源的字节数,一个是发生错误时的错误。Write(p []byte) (n int, err error)
}
  • io.Writer 表示一个写入器,它从缓冲区读取数据,并将数据写入目标资源。
  • 对于要用作编写器的类型,必须实现 io.Writer 接口的唯一一个方法 Write(p []byte)
  • 同样,只要实现了 Write(p []byte) ,那它就是一个编写器。

写文件:

func main() {file, err := os.Create("test.txt")if err != nil {log.Println("create error")}defer file.Close()b := make([]byte, 0)for i := 0; i < 10; i++ {b = append(b, byte(i))}file.WriteString(string(b))
}

2.3 bufio

  • bufio包实现了带缓冲区的读写,是对文件读写的封装
  • bufio缓冲写数据

模式

含义

os.O_WRONLY

只写

os.O_CREATE

创建文件

os.O_RDONLY

只读

os.O_RDWR

读写

os.O_TRUNC

清空

os.O_APPEND

追加

bufio读写数据

func wr() {// 参数2:打开模式,所有模式d都在上面// 参数3是权限控制// w写 r读 x执行   w  2   r  4   x  1//特殊权限位,拥有者位,同组用户位,其余用户位file, err := os.OpenFile("./xxx.txt", os.O_CREATE|os.O_WRONLY, 0666)if err != nil {return}defer file.Close()// 获取writer对象writer := bufio.NewWriter(file)for i := 0; i < 10; i++ {writer.WriteString("hello\n")}// 刷新缓冲区,强制写出writer.Flush()
}func re() {file, err := os.Open("./xxx.txt")if err != nil {return}defer file.Close()reader := bufio.NewReader(file)for {line, _, err := reader.ReadLine()if err == io.EOF {break}if err != nil {return}fmt.Println(string(line))}}func main() {re()
}


2.5 实现一个cat命令

使用文件操作相关知识,模拟实现linux平台cat命令的功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/714658.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【go语言开发】swagger安装和使用

本文主要介绍go-swagger的安装和使用&#xff0c;首先介绍如何安装swagger&#xff0c;测试是否成功&#xff1b;然后列出常用的注释和给出使用例子&#xff1b;最后生成接口文档&#xff0c;并在浏览器上测试 文章目录 安装注释说明常用注释参考例子 文档生成格式化文档生成do…

大模型生成,Open API调用

大模型是怎么生成结果的 通俗原理 其实&#xff0c;它只是根据上文&#xff0c;猜下一个词&#xff08;的概率&#xff09;…… OpenAI 的接口名就叫【completion】&#xff0c;也证明了其只会【生成】的本质。 下面用程序演示【生成下一个字】。你可以自己修改 prompt 试试…

【C++】类的转换函数

使用场景 C中当你创建了一个类&#xff0c;你想把这个类对象转换成基本类型的函数。类对象->基本类型对象 原理 如下实例&#xff0c;设计一个分数类&#xff0c;实现分数转换成double 浮点数的转换函数。并在mian函数隐式调用。 #include<iostream> class Fractio…

Python爬虫Cookies 池的搭建

Cookies 池的搭建 很多时候&#xff0c;在爬取没有登录的情况下&#xff0c;我们也可以访问一部分页面或请求一些接口&#xff0c;因为毕竟网站本身需要做 SEO&#xff0c;不会对所有页面都设置登录限制。 但是&#xff0c;不登录直接爬取会有一些弊端&#xff0c;弊端主要有…

南京师范大学计电院数据结构课设——排序算法

1 排序算法 1.1 题目要求 编程实现希尔、快速、堆排序、归并排序算法。要求首先随机产生10000个数据存入磁盘文件&#xff0c;然后读入数据文件&#xff0c;分别采用不同的排序方法进行排序并将结果存入文件中。 1.2 算法思想描述 1.2.1 随机数生成 当需要生成一系列随机数…

windows 11 前后端项目部署

目录 1.准备环境&#xff1a; 2.安装jdk 测试&#xff1a;winr 输入cmd 3.安装tomcat 4.安装mysql 远程导入数据&#xff1a; 外部后台访问&#xff1a;192.168.232.1:8080/crm/sys/loginAction.action?usernamezs&password123 5.安装nginx 前后端部署&#xff1…

qsort函数的模拟实现(冒泡排序模拟)

冒泡排序&#xff1a; 从第一个元素开始&#xff0c;依次比较相邻的两个元素&#xff0c;如果顺序不对就交换它们。 经过一轮遍历后&#xff0c;最大&#xff08;或最小&#xff09;的元素会排在最后。 重复进行上述步骤&#xff0c;直到没有任何元素需要交换&#xff0c;即…

爬虫入门到精通_实战篇8(分析Ajax请求并抓取今日头条美食美图)_界面上抓取Ajax方式

1 目标 目标&#xff1a; 抓取今日头条美食美图&#xff0c;如下&#xff1a; 一些网页直接请求得到的HTML代码并没有在网页中看到的内容&#xff0c;因为一些信息是通过Ajax加载&#xff0c;并通过js渲染生成的&#xff0c;这时就需要通过分析网页的请求来获取想要爬取的内容…

CSS 盒子模型(box model)

概念 所有HTML元素可以看作盒子&#xff0c;在CSS中&#xff0c;"box model"这一术语是用来设计和布局时使用CSS盒模型本质上是一个盒子&#xff0c;封装周围的HTML元素&#xff0c;它包括&#xff1a;外边距(margin)&#xff0c;边框(border)&#xff0c;内边距(pad…

关于 HTTP 协议,你了解多少

HTTP协议 FastAPI 是建立在 HTTP 协议之上&#xff0c;所以为了更好的掌握 FastAPI。我们需要先简单的了解一下 HTTP协议 简介 HTTP&#xff08;Hypertext Transfer Protocol&#xff09;遵循经典的客户端-服务器模型&#xff0c;客户端打开连接以发出请求&#xff0c;然后等…

【Go语言】Go语言中的流程控制

Go语言中的流程控制 流程控制主要用于设定计算执行的顺序&#xff0c;简历程序的逻辑结果&#xff0c;Go语言的流程控制语句与其他语言类似&#xff0c;支持如下几种流程控制语句&#xff1a; 条件语句&#xff1a;用于条件判断&#xff0c;对应的关键字有if、else和else if&a…

使用 Docker 部署 Fiora 在线聊天室平台

一、Fiora 介绍 Fiora 简介 Fiora 是一款开源免费的在线聊天系统。 GitHub&#xff1a;https://github.com/yinxin630/fiora Fiora 功能 注册账号并登录&#xff0c;可以长久保存你的数据加入现有群组或者创建自己的群组&#xff0c;来和大家交流和任意人私聊&#xff0c;并添…

MySQL 主从读写分离入门——基本原理以及ProxySQL的简单使用

一、读写分离工作原理 读写分离的工作原理&#xff1a;在大型网站业务中&#xff0c;当单台数据库无法满足并发需求时&#xff0c;通过主从同步方式同步数据。设置一台主服务器负责增、删、改&#xff0c;多台从服务器负责查询&#xff0c;从服务器从主服务器同步数据以保持一…

C语言数据结构——队列

目录 0.前言 1.队列的基本概念 2.队列的实现 2.1实现方式 2.2具体实现 3.队列的应用场景 4.一道队列的算法题&#xff08;LeetCode225. 用队列实现栈&#xff09; 5.结语 &#xff08;图像由AI生成&#xff09; 0.前言 在计算机科学领域&#xff0c;数据结构是组织和…

Linux篇: 进程控制

一、进程创建 1.1 fork函数初识 在Linux中&#xff0c;fork函数是非常重要的函数&#xff0c;它从已存在进程中创建一个新进程。新进程为子进程&#xff0c;而原进程为父进程。 返回值&#xff1a; 在子进程中返回0&#xff0c;父进程中返回子进程的PID&#xff0c;子进程创…

OSI七层模型/TCP四层模型

协议&#xff1a; 协议是双方共同指定的一组规则&#xff0c;在网络通信中表示通信双方传递数据和解释数据的一组规则。 从A上传文件到服务器B,需要在A和B之间制定一个双方都认可的规则&#xff0c;这个规则就叫文件传输协议&#xff0c;该协议是ftp协议的一个初级版本&#…

LeetCode 刷题 [C++] 第226题.翻转二叉树

题目描述 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 题目分析 深度优先搜索&#xff08;DFS&#xff09;- 递归方式 对于二叉树的镜像问题&#xff0c;很容易想到的就是使用递归来解决&#xff0c;自底向上依次翻转每一个节点…

2024年腾讯云优惠券领取页面_代金券使用方法_新老用户均可

腾讯云代金券领取渠道有哪些&#xff1f;腾讯云官网可以领取、官方媒体账号可以领取代金券、完成任务可以领取代金券&#xff0c;大家也可以在腾讯云百科蹲守代金券&#xff0c;因为腾讯云代金券领取渠道比较分散&#xff0c;腾讯云百科txybk.com专注汇总优惠代金券领取页面&am…

百度SEO快排原理是什么?如何快速排名方法?

前言&#xff1a;我之前说过我不打算写这个快速排序。 首先&#xff0c;我从来没有在自己的网站上操作过所谓的快速排序。 其次&#xff0c;我不能像网上很多人写的那样透露百度快速排序的秘密&#xff08;说实话&#xff0c;你可以透露秘密&#xff09;。 方法是有了&#xff…

Postman: 前端必备工具还是后端独享利器

Postman 的使用场景&#xff1a;适用于前端和后端 Postman 是一个流行的 API 测试与开发工具。它被广泛地应用在前后端开发的过程中&#xff0c;但是很多人对于它的使用场景存在疑惑。那么&#xff0c;到底是前端用还是后端用呢&#xff1f;本文将从多个角度详细解答这个问题。…