本文是全系列中第8 / 10篇:10节课学会Go
Interface
在Go
语言中,interface
是一种类型,用于定义一组方法签名。一个实现了这些方法的具体类型被称为这个interface
的实现类型。接口类型是一种抽象的类型,它不会暴露出所包含的具体值的内部结构和数据。
同时interface
类型可以代表任意类型的值,因此它可以用来定义不同类型的值。
定义接口
接口类型通常用于将具体类型的实现细节与实现类型的名称分离开来。它们可以帮助我们构建高度抽象的代码,使代码更加灵活、易于维护和扩展。同时,Go
语言中的接口类型也提供了一种非常强大的面向对象编程机制,使得Go
语言的面向对象编程变得更加自然和简单。
package main
import "fmt"
// Duck 接口类型 定义一组方法签名的集合
// 定义接口约定
type Duck interface {
GaGaga()
// ....
}
type DonaldDuck string
func (d DonaldDuck) GaGaga() {
fmt.Printf("%s, ga ga gan", d)
}
type RubberDuck string
func (d RubberDuck) GaGaga() {
fmt.Printf("%s, ga ga gan", d)
}
type Dog struct {
Name string
age int
}
func (d Dog) GaGaga() {
fmt.Printf("%s, ga ga gan", d.Name)
}
func main() {
var d Duck
d = DonaldDuck(" 唐老鸭")
d.GaGaga()
d = RubberDuck(" 小黄鸭")
d.GaGaga()
d = Dog{
Name: "小狗",
age: 5,
}
d.GaGaga()
}
以上代码定义了一个接口类型 Duck
,并定义了三个实现该接口的具体类型 DonaldDuck
、RubberDuck
和 Dog
。
其中,DonaldDuck
和 RubberDuck
都实现了 GaGaga()
方法,可以输出 xxx, ga ga ga
的字符串,而 Dog
类型也实现了 GaGaga()
方法,但是输出的是该狗的名字,而不是 ga ga ga
。
在 main
函数中,定义了一个 Duck
类型的变量 d
,并将 DonaldDuck
、RubberDuck
和 Dog
类型的变量赋值给 d
,这是因为这三个类型都实现了 Duck
接口,所以可以赋值给 Duck
类型的变量。
然后分别调用 d
的 GaGaga()
方法,根据不同的类型,输出不同的字符串。
接口断言
接口断言是指从一个接口类型中提取出具体的值和类型信息的操作。在 Go
中,接口断言可以使用类型断言的方式进行实现。如果一个接口变量 x
的底层类型是 T
类型,我们可以使用 x.(T)
的方式对其进行类型断言,其中 .(T)
表示将 x
转换为 T
类型。
package main
import "fmt"
// 类型断言
// 断言 interface
func main() {
var i interface{} = "hello"
s := i.(string)
fmt.Println(s)
// 类型断言, 断言失败一般会导致panic的发生, 所以为了防止panic的发生, 我们需要在断言前进行一定的判断。
// 如果断言失败, 那么ok的值将会是false
// 如果断言成功, 那么ok的值将会是true, 同时s将会得到正确类型的值。
s, ok := i.(string)
fmt.Println(s, ok)
f, ok := i.(float64)
fmt.Println(f, ok)
f = i.(float64) // 报错(panic)
fmt.Println(f)
}
在代码中,变量 i
的类型为 interface{}
,表示它可以保存任何类型的值。然后使用 .(string)
进行类型断言,将其转换为字符串类型,并将结果赋值给变量 s
。
接下来使用类型断言和布尔值的组合形式,将 i
断言为字符串类型,并将结果分别赋值给 s
和 ok
。由于 i
的实际类型是字符串类型,因此断言成功,ok
的值为 true
,s
得到了正确类型的值。
然后尝试将 i
断言为 float64
类型,这次断言失败,ok
的值为 false
,f
的值为 0
。
最后尝试将 i
直接断言为 float64
类型,由于实际类型是字符串类型,所以这次断言会导致 panic 异常的发生。
为什么需要接口
- 接口允许
Go
具有多态性, 在需要多态性的Go
中使用接口。 - 在可以传递多种类型的函数中,可以使用接口。
- 接口还用于帮助减少重复/样板代码。
在需要动态类型参数的函数和方法的情况下,接口非常有用,例如接受任何类型值的 Println
函数。
参考
https://blog.knoldus.com/how-to-use-interfaces-in-golang/
https://stackoverflow.com/questions/39092925/why-are-interfaces-needed-in-golang
https://stackoverflow.com/questions/23148812/whats-the-meaning-of-interface
https://blog.boot.dev/golang/golang-interfaces/