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() {
	// 实例化file
	f := 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 Sayer
	var y Mover

	var dog = Dog{"wangwang"}

	x = dog
	y = dog
	x.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 Mover
	move = d
	move.Move() //旺财 moving,....
	move = c
	move.Move() //小米 moving .....

}

接口嵌套

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

// Sayer 接口
type Sayer interface {
    say()
}

// Mover 接口
type Mover interface {
    move()
}

// 接口嵌套
type animal interface {
    Sayer
    Mover
}

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

type cat struct {
    name string
}

func (c cat) say() {
    fmt.Println("喵喵喵")
}

func (c cat) move() {
    fmt.Println("猫会动")
}

func main() {
    var x animal
    x = cat{name: "花花"}
    x.move()
    x.say()
}

1.5 空接口

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

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

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

func main() {
	var x interface{}
	var i = 100
	x = i
	fmt.Println(x) //100

	var name = "hhhhh"
	x = name
	fmt.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["小明"] = 100
	student["小红"] = "hahah"
	student["小高"] = false
	fmt.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["小明"] = 100
	student["小红"] = "hahah"
	student["小高"] = false
	fmt.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 []byte
	for {
		_, 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 []byte
	for {
		_, 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命令的功能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AutoSAR(基础入门篇)13.3-Mcal Dio配置

目录 一、Dio port配置 二、Dio pin配置 一、Dio port配置 同之前的Port一样,双击进入Dio配置界面后会看到几乎差不多的配置界面。General和Port类似,我们不再赘述,主要讲解Dio的配置 1. 其实Dio并没有什么实质的作用,主要起到了一个重命名的功能。双击DioConfig_0进入下…

优选算法|【双指针】|1089.复写零

目录 题目描述 题目解析 算法原理讲解 代码 题目描述 1089. 复写零 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的数组 就…

力扣hot100题解(python版41-43题)

41、二叉树的层序遍历 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]]示例…

STM32 GPIO的几种工作模式

介绍STM32 GPIO的几种工作模式 1、输出模式 STM32的引脚输出有两种方式&#xff1a; 1、推挽输出 2、开漏输出 1.1 推挽输出 当引脚设置为推挽输出时&#xff0c;P-MOS和N-MOS共同配合工作。 当使用HAL库 //该函数的作用就是将P-MOS导通&#xff0c;N-MOS关…

链式插补 (MICE):弥合不完整数据分析的差距

导 读 数据缺失可能会扭曲结果&#xff0c;降低统计功效&#xff0c;并且在某些情况下&#xff0c;导致估计有偏差&#xff0c;从而破坏从数据中得出的结论的可靠性。 处理缺失数据的传统方法&#xff08;例如剔除或均值插补&#xff09;通常会引入自己的偏差或无法充分利用数…

网页版图像处理软件开发服务:助您项目在市场竞争中脱颖而出

在当今数字化时代&#xff0c;图像处理在各个行业中扮演着重要的角色&#xff0c;虎克专注于提供定制化的网页版图像处理软件开发服务&#xff0c;为您的项目保驾护航。 1.网页版图像处理软件的定制化需求 1.1行业特定功能 针对不同的业务需求&#xff0c;深入了解行业特点&…

前端打包部署(黑马学习笔记)

我们的前端工程开发好了&#xff0c;但是我们需要发布&#xff0c;那么如何发布呢&#xff1f;主要分为2步&#xff1a; 1.前端工程打包 2.通过nginx服务器发布前端工程 前端工程打包 接下来我们先来对前端工程进行打包 我们直接通过VS Code的NPM脚本中提供的build按钮来完…

微信小程序云开发教程——墨刀原型工具入门(添加交互事件)

引言 作为一个小白&#xff0c;小北要怎么在短时间内快速学会微信小程序原型设计&#xff1f; “时间紧&#xff0c;任务重”&#xff0c;这意味着学习时必须把握微信小程序原型设计中的重点、难点&#xff0c;而非面面俱到。 要在短时间内理解、掌握一个工具的使用&#xf…

13 双口 RAM IP 核

双口 RAM IP 核简介 双口 RAM IP 核有两个端口&#xff0c;它又分为伪双端口 RAM 和真双端口 RAM&#xff0c;伪双端口 RAM 一个端口只能读&#xff0c;另一个端口只能 写&#xff0c;真双端口 RAM 两个端口都可以进行读写操作。同时对存储器进行读写操作时就会用到双端口 RAM…

LeetCode受限条件下可到达节点的数目

题目描述 现有一棵由 n 个节点组成的无向树&#xff0c;节点编号从 0 到 n - 1 &#xff0c;共有 n - 1 条边。 给你一个二维整数数组 edges &#xff0c;长度为 n - 1 &#xff0c;其中 edges[i] [ai, bi] 表示树中节点 ai 和 bi 之间存在一条边。另给你一个整数数组 restr…

决策树实验分析(分类和回归任务,剪枝,数据对决策树影响)

目录 1. 前言 2. 实验分析 2.1 导入包 2.2 决策树模型构建及树模型的可视化展示 2.3 概率估计 2.4 绘制决策边界 2.5 决策树的正则化&#xff08;剪枝&#xff09; 2.6 对数据敏感 2.7 回归任务 2.8 对比树的深度对结果的影响 2.9 剪枝 1. 前言 本文主要分析了决策树的分类和回…

matplotlib——散点图和条形图(python)

散点图 需求 我们获得北京2016年三月和十月每天白天最高气温&#xff0c;我们现在需要找出气温随时间变化的某种规律。 代码 # 导入库 from matplotlib import pyplot as plt import random# 解决中文乱码 import matplotlib matplotlib.rc("font",family"F…

详细讲解Docker架构的原理、功能以及如何使用

一、简介 1、了解docker的前生LXC LXC为Linux Container的简写。可以提供轻量级的虚拟化&#xff0c;以便隔离进程和资源&#xff0c;而且不需要提供指令解释机制以及全虚拟化的其他复杂性。相当于C中的NameSpace。容器有效地将由单个操作系统管理的资源划分到孤立的组中&…

如何解决线程安全问题(synchronized、原子性、产生线程不安全的原因,锁的特性,加锁的方式等等干货)

文章目录 &#x1f490;线程不安全的示例&#x1f490;锁的特性&#x1f490;产生线程不安全的原因&#xff1a;&#x1f490;加锁的三种方式 &#x1f490;线程不安全的示例 对于线程安全问题&#xff0c;这里用一个例子进行讲解&#x1f447;&#xff1a; 我现在定义一个变…

Image Fusion via Vision-Language Model【文献阅读】

阅读目录 文献阅读AbstractIntroduction3. Method3.1. Problem Overview3.2. Fusion via Vision-Language Model 4. Vision-Language Fusion Datasets5. Experiment5.1Infrared and Visible Image Fusion 6. Conclusion个人总结 文献阅读 原文下载&#xff1a;https://arxiv.or…

串及BF朴素查找算法(学习整理):

关于串的相关定义&#xff1a; 串&#xff1a;用‘ ’表示的字符序列空串&#xff1a;包含零个字符的串子串&#xff1a;包含传本身和空串的子串 eg: abc(,a,b,c,ab,bc,ac,abc)共7个&#xff1a;串的长度的阶乘1&#xff08;空串&#xff09;真子串&#xff1a;不包含自身的所…

Java进阶-IO(3)

话接上回&#xff0c;继续java IO的学习。上一次说完了字符流的读写数据&#xff0c;这次将基础部分剩余的一点内容看完。 一、流按功能分类 1、系统流 1.1 概述 系统流的类为 java.lang.System。Sytem 类封装了 Java 程序运行时的 3 个系统流。 System.in&#xff1a;标…

腾讯云幻兽帕鲁服务器中,如何检查并确保所有必要的配置文件(如PalWorldSettings.ini和WorldOption.sav)正确配置?

腾讯云幻兽帕鲁服务器中&#xff0c;如何检查并确保所有必要的配置文件&#xff08;如PalWorldSettings.ini和WorldOption.sav&#xff09;正确配置&#xff1f; 登录腾讯云控制台&#xff1a;登录轻量云控制台&#xff0c;找到部署了幻兽帕鲁的服务器&#xff0c;单击实例卡片…

基于BP-Adaboost的预测与分类,附MATLAB代码免费获取

今天为大家带来一期基于BP-Adaboost的预测与分类。代码中的BP可以替换为任意的机器学习算法。 原理详解 BP-AdaBoos模型先通过 AdaBoost集成算法串行训练多个基学习器并计算每个基学习 器的权重系数,接着将各个基学习器的预测结果进行线性组合,生成最终的预测结果。关于更多的原…

关于编写测试用例的一些思考

测试用例是QA同学的基本功&#xff0c;每个人都有一套编写测试用例的体系&#xff0c;本文是作者结合自身的工作经验以及阅读一些测试相关的书籍后的一些看法&#xff0c;欢迎大家一起讨论学习。 测试设计 测试用例格式 面试中一些常见的问题 1.APP测试与服务端测试的区别&am…