Go基础—6.接口

TOC

一、Go接口

在Go语言中,接口(interface)是一种抽象类型,它定义了一组方法的集合,只要一个类型实现了接口中定义的所有方法,那么它就隐式地实现了这个接口。
在Go语言中没有类和继承的概念,接口类型是对其它类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式我们可以让我们的函数更加灵活和更具有适应能力。
接口的常见用法

  • 多态:不同类型实现同一接口,实现多态行为。
  • 解耦:通过接口定义依赖关系,降低模块之间的耦合。
  • 泛化:使用空接口 interface{} 表示任意类型。

接口的声明使用

接口声明格式:

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

type 结构体名 struct {}
func 方法名1 (参数列表1) 返回值列表1 {/*...*/}
func 方法名2 (参数列表2) 返回值列表2 {/*...*/}
func 方法名3 (参数列表3) {/*...*/}

接口被实现的要求:

  • 接口的方法与实现接口的类型方法格式必须一致,函数名必须一致。
  • 实现接口的方法签名必须一致,签名包括方法中的名称、参数列表、返回参数列表。
  • 接口中的所有方法均被实现,当一个接口中有多个方法时,只有这些方法都被实现了,接口才能被正确编译并使用。
    下面是根据数据输出编写的一个接口功能的使用方式。
    【示例】代码如下所示:
package main

import "fmt"

// 定义一个数据输出的接口
type Print interface {
	PrintData(data interface{}) error
}

// 定义文件结构,用于实现PrintData
type print struct {
}

// 实现Print接口的PrintData方法
func (p print) PrintData(data interface{}) error {
	// 打印出数据
	fmt.Println("data的内容:", data)
	return nil
}

func main() {
	// 实例化print
	f := new(print)
	// 声明一个Print的接口,将接口赋值f,也就是*print类型
	var p Print = f
	// 使用PrintData接口进行数据写入
	p.PrintData("Hello, I used an interface.")
}
/* 运行结果如下
data的内容: Hello, I used an interface.
*/

空接口

空接口interface{}是Go的特殊接口,表示所有类型的集合。任意类型都可以实现空接口,常用于需要存储任意类型数据的场景。
【示例】代码如下所示:

package main

import "fmt"

func ValueType(value interface{}) {
	fmt.Printf("Value: %v, Type: %T\n", value, value)
}

func main() {
	ValueType("abc")
	ValueType(26)
	ValueType(3.14)
	ValueType([]int{1, 2})
	ValueType(func() {})
}
/* 运行结果如下
Value: abc, Type: string
Value: 26, Type: int
Value: 3.14, Type: float64
Value: [1 2], Type: []int
Value: 0x1020ede70, Type: func()
*/

动态值和动态类型

就是当赋值给变量为空接口,不固定类型的时候,使用Printf方法输出时改如何去获取这个值和类型。
接口变量实际上包含了两部分:

  • 动态类型(%T):接口变量存储的具体类型。
  • 动态值(%v):具体类型的值。
    【示例】代码如下所示:
var a interface{} = "hello"
fmt.Printf("动态类型: %T, 动态值: %v\n", a, a)
/* 运行结果如下
动态类型: string, 动态值: hello
*/

接口的零值

接口的零值是nil,不是0。当接口变量的动态类型和动态值都为 nil 时,接口变量为 nil。
【示例】代码如下所示:

var a interface{}
fmt.Println(a == nil)
/* 运行结果如下
true
*/

类型断言

类型断言用于从接口类型中提取其底层值。
不带检查的类型断言
不带检查就是在判断变量的类型不匹配的时候就会触发panic,直接报错。
判断语法:

var 变量名 interface{} = 变量值
value := 变量名.(判断变量类型)

比如判断一个变量的类型,代码如下:

var a, b interface{} = "abc", 2
fmt.Println(a.(string))
fmt.Println(b.(string))
/* 运行结果如下
abc
panic: interface conversion: interface {} is int, not string
*/

带检查的类型断言
为了避免panic,让程序可用性更高,我们可以使用带检查的类型断言。
判断语法:

var 变量名 interface{} = 变量值
value, ok := 变量名.(判断变量类型)

ok是一个布尔值,表示断言是否成功。如果断言失败,value为零值,ok为false。
【示例】代码如下所示:

var a interface{} = "abc"
if a_type, ok := a.(string); ok {
	fmt.Println("a是string类型,a的值为:", a_type)
} else {
	fmt.Println("a不是string类型")
}

类型选择

type switch 是 Go 中的语法结构,用于根据接口变量的具体类型执行不同的逻辑。
【示例】代码如下所示:

package main

import "fmt"

func ValueType(value interface{}) {
	switch v := value.(type) {
	case int:
		fmt.Println(v, "的类型是int")
	case string:
		fmt.Println(v, "的类型是string")
	case float64:
		fmt.Println(v, "的类型是float64")
	default:
		fmt.Println(v, "的类型未知")
	}
}

func main() {
	ValueType("abc")
	ValueType(26)
	ValueType(3.14)
	ValueType([]int{1, 2})
}
/* 运行结果如下
abc 的类型是string
26 的类型是int
3.14 的类型是float64
[1 2] 的类型未知
*/

接口嵌套组合

接口可以通过嵌套组合,实现更复杂的行为描述。
下面我们通过接口的嵌套把读取文件的接口(ReadFile)和写入文件的接口(WriteFile)组合成一个新的接口(ReadWriteFile)来实现读写文件的操作。
【示例】代码如下所示:

package main

import "fmt"

// 定义一个读取文件数据的接口
type ReadFile interface {
	RedPDF() string
	RedWord() string
}

// 定义一个写入数据到文件的接口
type WriteFile interface {
	WritePDF(data interface{}) error
	WriteWord(data interface{}) error
}

// 定义一个可读写的接口,内嵌读写两个接口
type ReadWriteFile interface {
	ReadFile
	WriteFile
}

// 定义文件结构,用于实现读写文件
type File struct {
	readwrite_limit int
}

// 模拟读取PDF和Word的函数方法
func (f File) RedPDF() string {
	fmt.Println("读写限制量:", f.readwrite_limit, "正在读取PDF文件...")
	return "I'm a PDF file."
}

func (f File) RedWord() string {
	fmt.Println("读写限制量:", f.readwrite_limit, "正在读取Word文件...")
	return "I'm a Wrod document."
}

// 模拟写入PDF和Word的函数方法
func (f File) WritePDF(data interface{}) error {
	fmt.Println("读写限制量:", f.readwrite_limit, "正在写入PDF文件...")
	fmt.Println("写入PDF文件内容:", data)
	return nil
}

func (f File) WriteWord(data interface{}) error {
	fmt.Println("读写限制量:", f.readwrite_limit, "正在写入Word文件...")
	fmt.Println("写入Word文件内容:", data)
	return nil
}

func main() {
	// 初始化ReadWriteFile接口对象,并赋值接口体的readwrite_limit变量
	var rw ReadWriteFile = File{readwrite_limit: 100}
	// 调用读取PDF和Word和写入PDF和Word的函数方法
	fmt.Println(rw.RedPDF())
	fmt.Println(rw.RedWord())
	rw.WritePDF("Hello,PDF.")
	rw.WriteWord("I like word.")
}
/* 运行结果如下
读写限制量: 100 正在读取PDF文件...
I'm a PDF file.
读写限制量: 100 正在读取Word文件...
I'm a Wrod document.
读写限制量: 100 正在写入PDF文件...
写入PDF文件内容: Hello,PDF.
读写限制量: 100 正在写入Word文件...
写入Word文件内容: I like word.
*/

一个模拟支付和退款的接口实例

下面示例我们根据模拟支付宝和微信两种平台的支付和退款,编写了支付类型和退款类型两个接口,再进一步地模拟了我们实现不同平台的支付和退款的情况进行使用接口。
【示例】代码如下所示:

package main

import "fmt"

// 支付类的接口定义
type Payment interface {
	Payment_begin(user string) string
	Payment_amount(amount float64) string
	Pay_success(user string, amount float64)
}

// 退款类的接口定义
type Refund interface {
	Refund(amount float64) string
	Refund_success(amount float64)
}

// 实现:支付宝的相关方法
type Alipay struct{}

func (a Alipay) Payment_begin(user string) string {
	return fmt.Sprintf("正在使用支付宝为用户 %s 充值", user)
}

func (a Alipay) Payment_amount(amount float64) string {
	return fmt.Sprintf("充值金额(单位:元): %.2f", amount)
}

func (a Alipay) Pay_success(user string, amount float64) {
	fmt.Printf("支付宝为用户 %s 充值 %.2f 元成功.\n", user, amount)
}

func (a Alipay) Refund(amount float64) string {
	return fmt.Sprintf("支付宝申请退款 %.2f 元", amount)
}

func (a Alipay) Refund_success(amount float64) {
	fmt.Printf("支付宝退款 %.2f 元成功.\n", amount)
}

// 实现:微信支付的相关方法
type WechatPay struct{}

func (w WechatPay) Payment_begin(user string) string {
	return fmt.Sprintf("正在使用微信为用户 %s 充值", user)
}

func (w WechatPay) Payment_amount(amount float64) string {
	return fmt.Sprintf("充值金额(单位:元): %.2f", amount)
}

func (w WechatPay) Pay_success(user string, amount float64) {
	fmt.Printf("微信为用户 %s 充值 %.2f 元成功.\n", user, amount)
}

func (w WechatPay) Refund(amount float64) string {
	return fmt.Sprintf("微信申请退款 %.2f 元", amount)
}

func (w WechatPay) Refund_success(amount float64) {
	fmt.Printf("微信退款 %.2f 元成功.\n", amount)
}

// 统一处理支付类操作的函数
func process_payment(p Payment, user string, amount float64) {
	fmt.Println(p.Payment_begin(user))
	fmt.Println(p.Payment_amount(amount))
	p.Pay_success(user, amount)
}

// 统一处理退款类操作的函数
func process_refund(r Refund, amount float64) {
	fmt.Println(r.Refund(amount))
	r.Refund_success(amount)
}

func main() {
    // 对不同的充值和退款接口进行实例化
	var (
		p1 Payment = Alipay{}
		p2 Payment = WechatPay{}
		r1 Refund  = Alipay{}
		r2 Refund  = WechatPay{}
	)
	// 模拟支付宝用户充值
	process_payment(p1, "2764653", 256.23)
	// 模拟微信用户充值
	process_payment(p2, "2344292", 467.5)
	// 模拟支付宝退款
	process_refund(r1, 172.2)
	// 模拟微信退款
	process_refund(r2, 284.88)
}
/* 运行结果如下
正在使用支付宝为用户 2764653 充值
充值金额(单位:元): 256.23
支付宝为用户 2764653 充值 256.23 元成功.
正在使用微信为用户 2344292 充值
充值金额(单位:元): 467.50
微信为用户 2344292 充值 467.50 元成功.
支付宝申请退款 172.20 元
支付宝退款 172.20 元成功.
微信申请退款 284.88 元
微信退款 284.88 元成功.
*/