1.Go 语言接口
在Go中,接口是一组方法签名(声明的是一组方法的集合)。当一个类型为接口中的所有方法提供定义时,它被称为实现该接口。它与oop非常相似。接口指定类型应具有的方法,类型决定如何实现这些方法。
让我们来看看这个例子: Animal
类型是一个接口,我们将定义一个 Animal
作为任何可以说话的东西。这是 Go 类型系统的核心概念:我们根据类型可以执行的操作而不是其所能容纳的数据类型来设计抽象。
type Animal interface {
Speak() string
}
非常简单:我们定义 Animal
为任何具有 Speak
方法的类型。Speak
方法没有参数,返回一个字符串。所有定义了该方法的类型我们称它实现了 Animal 接口。Go 中没有 implements
关键字,判断一个类型是否实现了一个接口是完全是自动地。让我们创建几个实现这个接口的类型:
type Dog struct {
}
func (d Dog) Speak() string {
return "Woof!"
}
type Cat struct {
}
func (c Cat) Speak() string {
return "Meow!"
}
type Llama struct {
}
func (l Llama) Speak() string {
return "?????"
}
type JavaProgrammer struct {
}
func (j JavaProgrammer) Speak() string {
return "Design patterns!"
}
我们现在有四种不同类型的动物:Dog
、Cat
、Llama
和 JavaProgrammer
。在我们的 main
函数中,我们创建了一个 []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}
,看看每只动物都说了些什么:
func main() {
animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}
for _, animal := range animals {
fmt.Println(animal.Speak())
}
}
package main
import "fmt"
type MyInt int
type MyInterface interface {
Print()
}
func (m MyInt) Print() {
fmt.Println(m)
}
func main() {
var i MyInterface = MyInt(5)
i.Print()
}
如果一个接口类型的方法集合包含了另一个接口类型的所有方法,那么这个接口类型就可以被赋值给另一个接口类型。
package main
import "fmt"
type animal interface {
eat()
sleep()
}
type chinaAnimal interface {
eat()
sleep()
run()
}
type Cat struct {
}
func (c *Cat) eat() {
fmt.Println("eat")
}
func (c *Cat) sleep() {
fmt.Println("sleep")
}
func (c *Cat) run() {
fmt.Println("run")
}
func main() {
var ani animal
var cAni chinaAnimal
// 接口chinaAnimal包含接口animal的所有方法,因此ani = cAni是合法的,而写成cAni = ani则会报错。
ani = cAni
ani = &Cat{}
ani.sleep()
}
2. 空接口
空接口 interface{}
是 Go 的特殊接口,表示所有类型的超集。
- 任意类型都实现了空接口。
- 常用于需要存储任意类型数据的场景,如泛型容器、通用参数等。
package main
import "fmt"
func printValue(val interface{}) {
fmt.Printf("Value: %v, Type: %T\n", val, val)
}
func main() {
printValue(42) // int
printValue("hello") // string
printValue(3.14) // float64
printValue([]int{1, 2}) // slice
}
3.类型断言
类型断言用于从接口类型中提取其底层值。
value := iface.(Type)
iface
是接口变量。Type
是要断言的具体类型。- 如果类型不匹配,会触发
panic
。
package main
import "fmt"
func main() {
var i interface{} = "hello"
str := i.(string) // 类型断言
fmt.Println(str) // 输出:hello
}
4.带检查的类型断言
为了避免 panic,可以使用带检查的类型断言:
alue, ok := iface.(Type)
ok
是一个布尔值,表示断言是否成功。- 如果断言失败,
value
为零值,ok
为false
。
package main
import "fmt"
func main() {
var i interface{} = 42
if str, ok := i.(string); ok {
fmt.Println("String:", str)
} else {
fmt.Println("Not a string")
}
}
5. 接口的零值
接口的零值是 nil。
当接口变量的动态类型和动态值都为 nil 时,接口变量为 nil。
接口零值示例:
package main
import "fmt"
func main() {
var i interface{}
fmt.Println(i == nil) // 输出:true
}