(8-3)接 口:内置接口

8.3  内置接口

为了帮助开发者提高开发效率,Go语言内置了一些实现常用功能的接口。在本节的内容中,将详细讲解几个常用的内置接口。

8.3.1  使用接口error处理错误

错误处理在每个编程语言中都是一项重要的内容之一,在开发中通常会遇到异常与错误这两种信息,Go语言中也不例外。在C语言中通过返回 -1 或者 NULL 之类的信息来表示错误,但是对于使用者来说,如果不查看相应的 API 说明文档,不清楚这个返回值究竟代表什么意思,比如返回 0 是成功还是失败?

针对这样的情况,Go语言引入了 error 接口类型作为错误处理的标准模式,如果函数要返回错误,则返回值类型列表中肯定包含 error。error 处理过程类似于C语言中的错误码,可逐层返回,直到被处理。

查看Go语言的源码时会发现 ,error 类型是一个非常简单的接口类型,代码如下所示:

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
    Error() string
}

在接口error中有一个签名为 Error() string 的方法,所有实现该接口的类型都可以当作一个错误类型。Error() 方法给出了错误的描述,在使用 fmt.Println()打印错误信息时,会在内部调用方法Error() string得到该错误的描述。

在一般情况下,如果函数需要返回错误,就将 error 作为多个返回值中的最后一个(但这并非是强制要求)。例如下面的代码(源码路径:Go-codes\8\cuo.go)演示了在函数中使用error接口返回错误信息的过程。

import (
	"errors"
	"fmt"
)

func divide(a, b float64) (float64, error) {
	if b == 0 {
		return 0, errors.New("divide by zero")
	}
	return a / b, nil
}

func main() {
	result, err := divide(10, 0)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(result)
	}
}

在上述代码中定义了函数divide(),它接收两个浮点数a和b作为参数,并返回它们的商以及一个错误对象。在函数内部,如果b等于0,则返回一个新的错误对象,该对象包含了一条错误消息"divide by zero"。否则,函数将计算a/b的值,并返回它们的商和一个空的错误对象。在主函数main()中调用divide,并检查其返回的错误对象是否为空。如果错误对象不为空,则说明divide函数执行过程中发生了错误,我们输出错误信息;否则,我们输出计算结果。执行后会输出:

divide by zero

通过这种方式,我们可以使用error接口来返回函数或方法执行过程中的错误信息,并根据需要进行处理。此外,标准库中还有许多实现了error接口的错误类型,比如os.PathError、net.Error等,可以帮助我们更好地处理各种类型的错误。

在Go语言中,还可以使用 error 接口自定义一个 Error()方法,来返回自定义的错误信息。

实例8-4:检查程序中的错误(源码路径:Go-codes\8\ziding.go

实例文件ziding.go的具体实现流程如下所示。

(1)定义一个自定义的error接口类型,并在该类型上定义了三个不同级别的错误.代码如下所示:

// 定义一个自定义的error接口类型
type CustomError interface {
    error
    Severity() int
}

// 定义三个不同级别的错误
type LowError struct {
    message string
}

func (e LowError) Error() string {
    return e.message
}

func (e LowError) Severity() int {
    return 1
}

type MediumError struct {
    message string
}

func (e MediumError) Error() string {
    return e.message
}

func (e MediumError) Severity() int {
    return 2
}

type HighError struct {
    message string
}

func (e HighError) Error() string {
    return e.message
}

func (e HighError) Severity() int {
    return 3
}

在上述代码中定义了一个CustomError接口类型,并在其上面定义了Severity()方法,用于返回错误的级别。然后定义了三个不同级别的错误:LowError、MediumError和HighError,并在它们上面分别实现了方法Error()和方法Severity()。

low error: empty data
medium error: data too short
high error: data contains error

(2)编写函数processData(),模拟处理输入数据时可能发生的三种错误。对应代码如下所示。

func processData(data []byte) error {
    if len(data) == 0 {
        return LowError{"empty data"}
    }
    if len(data) < 10 {
        return MediumError{"data too short"}
    }
    if bytes.Contains(data, []byte("error")) {
        return HighError{"data contains error"}
    }
    return nil
}

在函数processData()中,首先检查输入数据是否为空。如果是空的,则返回一个LowError级别的错误;否则,继续检查输入数据的长度是否小于10。如果太短,则返回一个MediumError级别的错误;否则,我们继续检查输入数据中是否包含字符串"error"。如果包含,则返回一个HighError级别的错误;否则,返回nil表示没有发生错误。

(3)最后定义主函数main()来调用上面的函数processData(),并根据不同级别的错误执行不同的操作。对应代码如下所示。

func main() {
    // 模拟处理三种不同的输入数据,并输出相应的结果
    for _, data := range [][]byte{nil, []byte("abc"), []byte("this is an error")} {
        err := processData(data)
        switch err.(type) {
        case LowError:
            fmt.Println("low error:", err)

        case MediumError:
            fmt.Println("medium error:", err)

        case HighError:
            fmt.Println("high error:", err)

        default:
            fmt.Println("no error")
        }
    }
}

在这个例子中,我们模拟处理了三种不同类型的输入数据(一个空的数组、一个长度小于10的数组和一个包含"error"字符串的数组),并使用类型分支来检查函数processData()返回的错误级别。如果返回的错误是LowError,则输出"low error:"并打印错误信息;如果是MediumError,则输出"medium error:"并打印错误信息;如果是HighError,则输出"high error:"并打印错误信息。如果没有发生错误,则输出"no error"。

8.3.2  使用接口sort.Interface实现排序

在Go语言中,在包sort中提供了对切片排序的函数和类型。要对一个切片进行排序,首先需要实现sort.Interface接口。在接口sort.Interface中包含如下三个方法:

  1. Len() int:返回该切片的长度。
  2. Less(i, j int) bool:根据索引i和j指定的元素比较它们的顺序,并返回一个布尔值来表示i是否应该排在j前面。
  3. Swap(i, j int):交换索引i和j指定的元素。

实例8-5:排序学生的年龄(源码路径:Go-codes\8\sort.go

实例文件sort.go的具体实现代码如下所示。

import (
	"fmt"
	"sort"
)

// 定义一个Person结构体类型
type Person struct {
	Name string
	Age  int
}

// 定义一个PersonSlice类型,包含多个Person对象,并实现sort.Interface接口
type PersonSlice []Person

func (p PersonSlice) Len() int {
	return len(p)
}

func (p PersonSlice) Less(i, j int) bool {
	return p[i].Age < p[j].Age
}

func (p PersonSlice) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

// 模拟一个人员名单,并对其进行排序
func main() {
	// 创建一个包含多个Person对象的切片
	people := PersonSlice{
		{"Alice", 25},
		{"Bob", 30},
		{"Charlie", 20},
	}

	// 对该切片进行排序
	sort.Sort(people)

	// 输出排序后的结果
	for _, person := range people {
		fmt.Println(person.Name, person.Age)
	}
}

对上述代码的具体说明如下:

  1. 首先定义了结构体类型Person,并在该类型上定义了方法Less(),用于按照年龄从小到大的顺序比较两个Person对象。然后定义了一个PersonSlice类型,包含多个Person对象,并实现了接口sort.Interface的三个方法。这样,就可以对该切片进行排序了。
  2. 在主函数main()中创建了一个包含多个Person对象的切片,并使用函数sort.Sort()对其进行排序。通过这种方式,可以方便地对任何实现了sort.Interface接口的切片进行排序,而不需要直接操作底层数据。

执行后会输出:

Charlie 20
Alice 25
Bob 30

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值