原理
- 方法集与接收器类型:
- 值接收器实现的方法,该类型的指针和值都可以调用这些方法,因为Go语言会自动进行解引用转换。
- 指针接收器实现的方法,只有该类型的指针可以调用,因为方法内部可能会修改接收器的值,而值类型的副本无法实现这种修改。
- 类型断言:类型断言用于检查接口值的动态类型是否为特定类型,并获取其具体值。如果断言成功,可以安全地调用该具体类型的方法。
- 接口转换:接口转换是将一个接口值转换为另一个接口类型,前提是原接口值的动态类型实现了目标接口。在转换过程中,会检查动态类型是否满足目标接口的方法集。
代码示例
package main
import (
"fmt"
)
// 定义接口I
type I interface {
Method() string
}
// 定义结构体S1,使用值接收器实现接口I
type S1 struct {
Name string
}
func (s S1) Method() string {
return "S1: " + s.Name
}
// 定义结构体S2,使用指针接收器实现接口I
type S2 struct {
Name string
}
func (s *S2) Method() string {
return "S2: " + s.Name
}
func main() {
// 创建接口值的切片
var interfaces []I
s1 := S1{Name: "value1"}
s2 := &S2{Name: "value2"}
interfaces = append(interfaces, s1, s2)
for _, i := range interfaces {
// 类型断言
if s1, ok := i.(S1); ok {
// 这里s1是S1类型的值,因为是值接收器实现的方法,所以可以直接调用
fmt.Println("Type assertion to S1:", s1.Method())
} else if s2, ok := i.(*S2); ok {
// 这里s2是*S2类型的指针,因为S2使用指针接收器实现方法,所以只能指针调用
fmt.Println("Type assertion to *S2:", s2.Method())
}
// 接口转换
var newI I
if s1, ok := i.(S1); ok {
newI = s1
// 这里将S1类型的值转换为接口I,由于S1实现了I,所以可以转换
fmt.Println("Interface conversion from S1 to I:", newI.Method())
} else if s2, ok := i.(*S2); ok {
newI = s2
// 这里将*S2类型的指针转换为接口I,由于*S2实现了I,所以可以转换
fmt.Println("Interface conversion from *S2 to I:", newI.Method())
}
}
}
代码注释
- 定义接口和结构体:
- 定义接口
I
有一个方法 Method
。
S1
使用值接收器实现 Method
方法。
S2
使用指针接收器实现 Method
方法。
- 创建接口值切片:
- 创建
S1
的值和 S2
的指针,并将它们添加到 interfaces
切片中,该切片类型为 []I
。
- 类型断言:
- 使用
i.(S1)
尝试将接口值 i
断言为 S1
类型的值,如果断言成功,则调用 S1
的 Method
方法。
- 使用
i.(*S2)
尝试将接口值 i
断言为 *S2
类型的指针,如果断言成功,则调用 *S2
的 Method
方法。
- 接口转换:
- 如果断言
i
为 S1
类型的值成功,则将 S1
值转换为接口 I
,并调用 Method
方法。
- 如果断言
i
为 *S2
类型的指针成功,则将 *S2
指针转换为接口 I
,并调用 Method
方法。这样确保了方法集的正确调用以及接口实现的一致性。