当前位置: 首页 > article >正文

golang学习笔记22-面向对象(四):接口【重要】

本节也是GO核心部分,很重要。
注:由于导包语句已经在19讲(笔记19:面向对象的引入)展示过了,所以这里就不展示了。

一、定义与实现

(1)接口中可以定义一组方法,但不需要实现,不需要方法体。并且接口中不能包含任何变量。到某个自定义类型要使用的时候(实现接口的时候),再根据具体情况把这些方法具体实现出来
(2)实现接口要实现所有的方法才算实现
(3)Golang不需要显式的实现接口。Golang中没有implement关键字,Golang中实现接口是基于方法的,不是基于接口的,例如:
A接口a,b方法
B接口a,b方法
C结构体实现了a,b方法,那么C实现了A接口,也可以说实现了B接口,只要实现全部方法即可,和实际接口耦合性很低,比Java松散得多。
(4)接口目的是为了定义规范,具体由别人来实现即可。
示例,首先在utils中定义接口,结构体及方法:

//接口的定义:定义一组规则或规范,由别人实现
type SayHello interface {
	Say() //声明没有实现的方法
}

//接口的实现:1.定义类型,这里是结构体
type Chinese struct {
}
type American struct {
}

//2.实现接口的方法(具体实现)
func (c Chinese) Say() {
	fmt.Println("你好")
}

func (a American) Say() {
	fmt.Println("hello")
}

//定义一个函数,专门用于各国人打招呼,接收(被实现的)接口变量
func Greet(s SayHello) {
	s.Say()
}

然后在main中调用:

func main() {
	c := utils.Chinese{}
	a := utils.American{}
	utils.Greet(c)
	utils.Greet(a)
}

可能有些人看到我每次调用utils觉得麻烦,那么这里就提一个问题:如果用别名,比如定义utils.Chinese为Chinese,utils.Greet(c)还有效吗?答案是否定的,因为编译器不认为这两个是同一类型,自然也就不认为新的类型实现了接口的方法,所以别名定义后,新类型也必须实现接口的方法!

二、一些细节

【1】接口本身不能创建实例,比如在Greet中添加以下代码会有警告:

var ss SayHello
ss.Say()

警告:nil dereference in dynamic method call,这表示ss是空引用,空引用是无法进行任何操作的,所以运行原来main中的代码一定会报错。
但是接口可以指向一个实现了该接口的变量,比如在main中添加如下代码:

var s utils.SayHello = c
s.Say()

这里s接收的不再是空引用,而是实现了接口的结构体,所以能调用接口的方法,注意该类型(结构体)一定要实现方法,不实现没法赋值。
【2】只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。示例,uitils中的定义为:

type MyInt int

func (i MyInt) Say() {
	fmt.Println("hello", i)
}

main中的调用为:

func main() {
	var i utils.MyInt = 10
	i.Say()
}

【3】一个自定义类型可以实现多个接口,示例:
uitils中的定义为:

type InterA interface {
	A1()
}
type InterB interface {
	B1()
}
type Stu struct {
}

func (s Stu) A1() {
	fmt.Println("run A1")
}
func (s Stu) B1() {
	fmt.Println("run B1")
}

main中的调用为:

func main() {
	s := utils.Stu{}
	s.A1()
	s.B1()
}

【4】一个接口可以继承多个接口,这时如果要实现该接口,必须将继承接口的方法都实现。示例,uitils中的定义为:

type InterC interface {
	InterA
	InterB
	C1()
}

func (s Stu) C1() {
	fmt.Println("run C1")
}

main中的调用为:

func main() {
	s := utils.Stu{}
	s.A1()
	s.B1()
	s.C1()
}

【5】空接口可以接收所有类型(未实现的非空接口除外),可以理解为所有类型都实现了空接口。由于这个比较简单,我就直接在main中写了,用到了可变参数,涉及的知识点丰富:

func printInf(inf ...interface{}) {
	for _, value := range inf {
		fmt.Printf("%T ", value)
	}
}
func main() {
	// 整,浮,字符串,布,数组,函数,指针,切片,映射,结构体
	intg, float, str, b, arr := 1, 1.1, "1", true, [1]int{1}
	pointer, slice, fun := new(int), arr[:], func() {}
	mapp, stct := make(map[string]int, 2), utils.Stu{}

	// 创建一个切片包含所有变量
	inf := []interface{}{intg, float, str, b, arr, pointer, slice, fun, mapp, stct}

	// 传递切片作为参数,注意加上...将切片展开
	printInf(inf...)
}

程序输出为:

int
float64
string
bool
[1]int
*int
[]int
func()
map[string]int
utils.Stu

http://www.kler.cn/a/327033.html

相关文章:

  • Pion WebRTC 项目教程
  • 数据结构(Java版)第六期:LinkedList与链表(一)
  • 从 GitLab.com 到 JihuLab.com 的迁移指南
  • 如何在 Ubuntu 22.04 上安装以及使用 MongoDB
  • Spring Boot 项目创建
  • 帧缓存的分配
  • 优化后的版本
  • 家用无线路由器配置
  • 大语言模型之LlaMA系列-LlaMA 2及LlaMA_chat(下)
  • c++(AVL树及其实现)
  • 在线PDF怎么转换成JPG图片?分享14种转换操作!
  • 【系统架构】服务端高并发分布式结构演进之路
  • 使用PYTHONPATH的注意事项
  • C++11中智能指针以及标准模板库 My_string My_stack
  • Zabbix 7.0 图表中文乱码问题处理步骤
  • 后台数据管理系统 - 项目架构设计-Vue3+axios+Element-plus(0926)
  • leetcode刷题day27|贪心算法Part01(455.分发饼干、376. 摆动序列、53. 最大子序和)
  • 两个向量所在平面的法线,外积,叉积,行列式
  • GIT安装及集成到IDEA中操作步骤
  • Linux基础命令mount,umount详解
  • jmeter进行性能测试实践
  • 查看 .so 库(共享对象库)的依赖
  • linux驱动编程——等待队列
  • 显示器放大后,大漠识图识色坐标偏移解决方法
  • 【leetcode】122. 买卖股票的最佳时机 II
  • Linux下路由信息探测traceroute