Golang中如何强制类型检查并判断对象是否实现了接口?
Golang中如何强制类型检查并判断对象是否实现了接口? 以下代码片段令人困惑。问题是如何确保
package main
import (
"fmt"
)
type PersonAttributes struct {
firstName string
}
type Behaviour interface {
Run()
Walk()
}
type Manager struct {
mapper map[string]interface{}
}
func NewManager() Manager {
return Manager{
mapper: make(map[string]interface{}),
}
}
func (m *Manager) AddToManager(d interface{}) {
m.mapper["a"] = d
}
func NewPerson() PersonAttributes {
return PersonAttributes{
firstName: "Atul",
}
}
func (s *PersonAttributes) Run() {
fmt.Println("Running")
}
func (s *PersonAttributes) Walk() {
fmt.Println("Walking")
}
func main() {
obj := NewPerson()
manager := NewManager()
manager.AddToManager(obj)
//这段代码正在运行且预期会运行
//obj.Run()
//尽管Person定义了行为,但这仍然失败
if rt, okk := manager.mapper["a"].(Behaviour); okk {
//问题1:为什么这个代码块没有运行?
//问题2:如何让这个代码块运行?
rt.Run()
}
}
更多关于Golang中如何强制类型检查并判断对象是否实现了接口?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
非常感谢 @acim。现在非常清楚了。
更多关于Golang中如何强制类型检查并判断对象是否实现了接口?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
非常感谢。我正在尝试深入理解这个问题。
为什么这里的返回类型作为指针很重要? 传递的是"哪个"对象为什么会产生影响? 为什么不是任何实现了这些函数的对象都能通过类型检查?
这是因为 NewPerson() 返回的是 PersonAttributes 而不是 *PersonAttributes。
如果将 NewPerson() 修改为以下形式,就能正常运行:
func NewPerson() *PersonAttributes {
return &PersonAttributes{
firstName: "Atul",
}
}
您将值类型 PersonAttributes 添加到 Manager 中,而您的方法定义在指针接收器上,这意味着值接收器上没有行为方法。您需要在 PersonAttributes(而不是 *PersonAttributes)上定义 Run 和 Walk 方法,或者使用指针:
func NewPerson() *PersonAttributes {
return &PersonAttributes{
firstName: "Atul",
}
}
顺便提一下,在 Go 语言中,名为 NewSomething 的函数通常返回指针。
我在上面的帖子中加了更多注释,但可能还是不够清楚。在Go语言中,你可以为值类型或指针类型定义方法,这两者并不相同。如果你像现在这样给指针接收器添加方法,那么你需要用指针而不是值来检查接口实现。反之,如果你为值接收器定义方法,则应该用值来检查接口。
不要混用方法类型,如果你有指针接收器的方法,就让该对象的所有方法都基于指针定义,反之亦然。
最后,Go语言中的NewSomething函数应该返回指针而不是值,这才是预期的行为。
func main() {
fmt.Println("hello world")
}
在Go语言中,类型断言失败是因为PersonAttributes的方法接收器是指针类型,而存储在manager.mapper["a"]中的是值类型。
问题分析
当调用NewPerson()时返回的是PersonAttributes值类型,而Run()和Walk()方法的接收器是*PersonAttributes(指针类型)。这意味着:
PersonAttributes值类型不实现Behaviour接口*PersonAttributes指针类型实现Behaviour接口
解决方案
方案1:存储指针到Manager中
func main() {
obj := NewPerson()
manager := NewManager()
// 存储指针而不是值
manager.AddToManager(&obj)
if rt, okk := manager.mapper["a"].(Behaviour); okk {
rt.Run() // 现在这会正常运行
}
}
方案2:修改NewPerson返回指针
func NewPerson() *PersonAttributes {
return &PersonAttributes{
firstName: "Atul",
}
}
func main() {
obj := NewPerson() // obj现在是指针类型
manager := NewManager()
manager.AddToManager(obj)
if rt, okk := manager.mapper["a"].(Behaviour); okk {
rt.Run() // 这会正常运行
}
}
方案3:使用值接收器定义方法
// 将方法接收器改为值类型
func (s PersonAttributes) Run() {
fmt.Println("Running")
}
func (s PersonAttributes) Walk() {
fmt.Println("Walking")
}
func main() {
obj := NewPerson() // 值类型
manager := NewManager()
manager.AddToManager(obj)
if rt, okk := manager.mapper["a"].(Behaviour); okk {
rt.Run() // 现在这会正常运行
}
}
类型检查的最佳实践
// 明确的类型检查函数
func CheckBehaviour(obj interface{}) (Behaviour, bool) {
if behaviour, ok := obj.(Behaviour); ok {
return behaviour, true
}
return nil, false
}
// 或者在Manager中添加方法
func (m *Manager) GetBehaviour(key string) (Behaviour, bool) {
if obj, exists := m.mapper[key]; exists {
if behaviour, ok := obj.(Behaviour); ok {
return behaviour, true
}
}
return nil, false
}
// 使用示例
func main() {
obj := NewPerson()
manager := NewManager()
manager.AddToManager(&obj) // 存储指针
if behaviour, ok := manager.GetBehaviour("a"); ok {
behaviour.Run()
behaviour.Walk()
}
}
关键点:在Go中,值类型和指针类型在实现接口时被认为是不同的类型。如果方法使用指针接收器定义,只有该类型的指针才能满足接口要求。

