Go 语言入门很简单 -- 13. Go 接口 #私藏项目实操分享#

简介: Go 语言入门很简单 -- 13. Go 接口 #私藏项目实操分享#

前面的文章中,了解到 Go 语言不是一种传统意义上的面向对象语言,因此 Go 没有类和继承的概念。

但是面向对象的功能很强大而且很实用, 前一篇文章中已经了解到可以通过嵌入类型来实现 Has-a 的关系。

这一篇文章将通过学习接口来看到 Go 通过结构体、方法和接口实现面向对象的功能。

在上一篇文章 《 12. Go 方法》 中,我们注意到命名了求矩形的面积的 area() 方法和圆面积的 area() 方法,在实际开发过程中,类似的关系还有很多。Go 语言通过 接口 类型将这些相似直接表示出来。

接口为 Go 语言实现了抽象的功能。接口的主体只能有方法声明和嵌入接口。接口中的方法没有函数体。只能在接口中定义方法签名,我们不能创建接口的对象,接口只为了实现,任何实现类型都需要提供方法的主体。

接口是定义一系列动作和行为的工具。它们帮助对象依赖抽象而不是其他对象的具体实现。我们可以通过对多个接口进行分组来组合不同的行为。

image.png

什么是接口

简单来说,接口是一组方法的组合,代表不同数据类型的常见行为。

通过接口,我们可以组织不同的方法组,适用于不同类型的对象。通过这样做,我们的程序可以依靠更高的抽象(接口),而不是具体的实现,允许其他方法与实现同一接口的各种不同对象一起工作。在面向对象的世界里,这个概念被称为依赖倒置原则(Dependency Inversion PrincipleSOLID)。

在 Go 中,构建小型接口然后将它们组合在一起以向对象添加更多功能被认为是最佳实践。通过这种方式,您可以保持代码整洁并提高可重用性。

在 Go 中,我们可以在一个结构体(对象)实现其所有方法时自动推断它实现了一个接口。如果某个对象实现了某个接口的所有方法,就表示它实现了该“接口”,无须显式地在该类型上添加接口说明。

定义一个接口

假设我们把圆和矩阵抽象出来 -- 都是形状,因此可以实现一个形状 Shape 的接口,代码如下:

type Shape interface {
    perimeter() float64
    area() float64
}

可以看到接口的定义也是 先使用 type 关键字,中间是接口名,后面是关键字 interface。但接口里面的主体不是定义字段,而是定义一些“方法集”。

方法集是一系列方法,为了去实现这个接口。即只定义原型而不用实现。

总结,定义接口的基本格式如下:

type interfaceName interface {
  methodName1(参数列表) (返回值)
  methodName2(参数列表) (返回值)
  ...
 }

Shape 是一个非常简单的接口,它定义了一个 area()  的方法。此方法表示其他对象可以实现的动作或行为。

在矩形和圆结构体中,都有返回面积( float )的方法,所以都可以实现 Shape

Note:

1. Go 语言中,接口命名时习惯性以“er”结尾,比如:Printer、Reader、Writer等,通常以动名词来命名。

2. Go 语言中,一个接口中包含的方法不应该过多,0~3 个即可。

例如:

package main
import "fmt"
// 定义结构体部分
type Person struct {
  Name string
}
type Student struct {
  Person
  School string
}
type Teacher struct {
  Person
  Department string
}
// 定义接口部分
type Speaker() interface {
  Talk()
}
type Learner interface {
  Talk()
  Study()
}
// 方法实现接口部分
func (p Person) Talk(){}
func (s Student) Talk(){}
func (t Student) Talk(){}
func (s Student) Study(){}

接口组合

在 Go 语言中,除了类型可以匿名组合,接口也可以组合在一起。将一个接口匿名嵌入到另一个接口中,就是接口组合(Interface-combination)

例如:

type SpeakLearner interface {
  Speaker
  Learner
}

在接口 SpeakLearner 中组合了 SpeakerLearner 两个接口,所以它既能做 Speaker 接口的所有事情,又能做 Learner 接口的所有事情。

接口的赋值

与其他面向对象编程语言不同,Go 语言的接口还可以赋值,如果定义了一个接口的变量,那么这个变量可以存储实现了这个接口的任意类型的对象。

例如上述的 Speaker 接口就可以存储对象 Person、Teacher 和 Student 的值。

package main
import "fmt"
// 定义结构体部分
type Person struct {
    Name string
}
type Student struct {
    Person
    School string
}
type Teacher struct {
    Person
    Department string
}
// 定义接口部分
type Speaker interface {
    Talk()
}
type Learner interface {
    Talk()
    Study()
}
// 对象方法实现
func (p Person) Talk() {
    fmt.Printf("Hi, 我是 %s. 你好~\n", p.Name)
}
func (s Student) Talk() {
    fmt.Printf("Hi, 我是 %s. 我是学生,正在 %s 学习.\n ", s.Name, s.School)
}
func (t Teacher) Talk() {
    fmt.Printf("Hi, 我是 %s. 我是老师,正在 %s 工作.\n", t.Name, t.Department)
}
func (s Student) Study() {
    fmt.Printf("我正在 %s 学习 Go 语言.\n", s.School)
}
func main() {
    p1 := Person{"张三"}
    t1 := Teacher{Person{"Lily"}, "计算机学院"}
    s1 := Student{Person{"Lee"}, "搬砖大学"}
    var s Speaker // 定义 Speaker 类型的变量 s
    s = p1        // s 能存储 People
    s.Talk()
    s = t1 // s 能存储 Teacher
    s.Talk()
    s = s1 // s 能存储 Student
    s.Talk()
    var l Learner
    l = s1 // l 能存储 Student
    l.Study()
}

运行结果为:

Hi, 我是 张三. 你好~
Hi, 我是 Lily. 我是老师,正在 计算机学院 工作.
Hi, 我是 Lee. 我是学生,正在 搬砖大学 学习.
我正在 搬砖大学 学习 Go 语言.

接口作为函数的参数

我们可以将接口类型作为函数的参数。

func totalArea(shapes ...Shape) float64 {
    var area float64
    for _, s := range shapes {
        area += s.area()
    }
    return area
}

可以这样调用函数:

fmt.Println(totalArea(&c, &r))

接口也可被用于字段:

type MultiShape struct {
    shapes []Shape
}

我们甚至可以把 MultiShape 本身变成一个 Shape,通过给它一个 它的面积方法。

func (m *MultiShape) area() float64 {
    var area float64
    for _, s := range m.shapes {
        area += s.area()
    }
    return area
}

现在, Multishape 可以包括 Circle s, Rectangle s 或者其他 MultiShape s.

空接口

在 Go 语言中,任何数据类型都默认实现了空接口,空接口可以这样定义:

type InterfaceName interface{
}

空接口也就是包含 0 个方法的接口,所以可以使用空接口定义任何类型的变参函数,如果一个函数返回空接口,就可以返回任意类型的值。

interface{}
// 函数 test1 返回 1 个interface{}
func test1(a interface{}) {}
// 函数 test2 可返回多个 interface{}
func test2(a ... interface{}) {}

空接口可以存储结构体、字符串、整数等任何类型。空接口增强了代码的扩展性与通用性。

Notes:

平时使用的输入输出函数 fmt.Println 的参数就是一个空接口,正因为如此,Println 函数可以打印多种类型

func Println(a ...interface{}) (n int, err error)

Go 语言中的空接口的作用类似于 C 语言中的 void * 、Java/C# 中的 System.Object

接口的比较

两个接口可以通过 == 或 != 进行比较,例如:

var a, b interface{}
fmt.Println(a == b )

接口的比较规则:

  • 动态值为 nil 的接口变量总是相等的
  • 如果只有 1 个接口为 nil,那么比较结果总是 false
  • 如果两个接口不为 nil 且接口变量具有相同的动态类型和动态类型值,那么两个接口是相同的
  • 如果接口存储的动态类型值是不可比较的,那么在运行时会报错

总结

  • 一个接口中的方法没有主体。一个接口的所有方法都是纯粹的抽象的。
  • 一个接口的方法可以有参数和返回类型。任何自定义类型都可以实现该接口。
  • 没有明确的语法来实现一个接口。当任何类型定义了接口的所有方法,那么该类型就隐含实现了该接口。
  • 一个实现了接口的类型也可以有其他方法
  • 一个类型可以实现一个以上的接口
  • 一个接口可以由许多类型实现
  • 当一个接口没有任何方法时,它被称为空接口。一个空接口在默认情况下是由所有类型实现
  • 一个接口可以作为结构的一个字段使用。使用时,我们需要在创建结构对象时传递接口的实际实现。创建结构体对象时传递接口的实际实现。
  • 在 Go 中,多态性可以用一个接口来实现。

相关文章
|
2月前
|
算法 Java Go
【GoGin】(1)上手Go Gin 基于Go语言开发的Web框架,本文介绍了各种路由的配置信息;包含各场景下请求参数的基本传入接收
gin 框架中采用的路优酷是基于httprouter做的是一个高性能的 HTTP 请求路由器,适用于 Go 语言。它的设计目标是提供高效的路由匹配和低内存占用,特别适合需要高性能和简单路由的应用场景。
237 4
|
3月前
|
Linux Go iOS开发
Go语言100个实战案例-进阶与部署篇:使用Go打包生成可执行文件
本文详解Go语言打包与跨平台编译技巧,涵盖`go build`命令、多平台构建、二进制优化及资源嵌入(embed),助你将项目编译为无依赖的独立可执行文件,轻松实现高效分发与部署。
|
2月前
|
Cloud Native 安全 Java
Go语言深度解析:从入门到精通的完整指南
🌟蒋星熠Jaxonic,Go语言探索者。深耕云计算、微服务与并发编程,以代码为笔,在二进制星河中书写极客诗篇。分享Go核心原理、性能优化与实战架构,助力开发者掌握云原生时代利器。#Go语言 #并发编程 #性能优化
404 43
Go语言深度解析:从入门到精通的完整指南
|
2月前
|
Java 编译器 Go
【Golang】(5)Go基础的进阶知识!带你认识迭代器与类型以及声明并使用接口与泛型!
好烦好烦好烦!你是否还在为弄不懂Go中的泛型和接口而烦恼?是否还在苦恼思考迭代器的运行方式和意义?本篇文章将带你了解Go的接口与泛型,还有迭代器的使用,附送类型断言的解释
173 3
|
3月前
|
Cloud Native 安全 Java
Go语言深度解析:从入门到精通的完整指南
🌟 蒋星熠Jaxonic,执着的星际旅人,用Go语言编写代码诗篇。🚀 Go语言以简洁、高效、并发为核心,助力云计算与微服务革新。📚 本文详解Go语法、并发模型、性能优化与实战案例,助你掌握现代编程精髓。🌌 从goroutine到channel,从内存优化到高并发架构,全面解析Go的强大力量。🔧 实战构建高性能Web服务,展现Go在云原生时代的无限可能。✨ 附技术对比、最佳实践与生态全景,带你踏上Go语言的星辰征途。#Go语言 #并发编程 #云原生 #性能优化
|
5月前
|
JSON 中间件 Go
Go语言实战指南 —— Go中的反射机制:reflect 包使用
Go语言中的反射机制通过`reflect`包实现,允许程序在运行时动态检查变量类型、获取或设置值、调用方法等。它适用于初中级开发者深入理解Go的动态能力,帮助构建通用工具、中间件和ORM系统等。
306 63
|
Java 编译器 Go
一起学Golang系列(五)初次接触Go语言可能遇到的各种坑!
前面介绍了Go语言的基础语法,所谓磨刀不误砍柴工,希望大家还是能熟悉掌握这些基础知识,这样后面真正学起Go来才会得心应手。 作为初学者。Go语言的语法有些和java类似,但也有很多不一样的地方。刚开始都会遇到各种各样的坑。下面就来总结下学习go语言的过程中,遇到的各种坑。
一起学Golang系列(五)初次接触Go语言可能遇到的各种坑!
|
2月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
179 1
|
4月前
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
291 1
|
4月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
382 0