go - 如何使用其中一种方法返回同一接口的对象的接口?
问题描述
我仍在思考 Go 中的接口,但我正在编写一个包,它将获取结构图并将它们作为树打印到标准输出。为此,我定义了一个interface
被调用TreeNode
的方法,它包含一个GetChildren
需要返回一个实现的结构切片的方法TreeNode
。这样我就可以从根开始向下递归树。即这里是完整的接口定义:
type TreeNode interface {
GetName() string
GetChildren() []TreeNode
}
我听说这是“接受接口并返回结构”的最佳实践。这很有意义。但是,当我尝试在struct
. 这是一个完整的例子:
package main
import "fmt"
type MyStruct struct {
Name string
Children []*MyStruct
}
type TreeNode interface {
GetName() string
GetChildren() []TreeNode
}
func PrintTree(root TreeNode) {
// Print the nodes recursively
fmt.Println(root.GetName())
// etc.
}
func main() {
child1 := &MyStruct{
Name: "Child 1",
}
child2 := &MyStruct{
Name: "Child 2",
}
root := &MyStruct{
Name: "Root",
Children: []*MyStruct{child1, child2},
}
PrintTree(root)
}
func (my_struct *MyStruct) GetName() string {
return my_struct.Name
}
func (my_struct *MyStruct) GetChildren() []*MyStruct {
return my_struct.Children
}
PrintTree
在编译器的调用中main
抱怨说
cannot use root (type *MyStruct) as type TreeNode in argument to PrintTree:
*MyStruct does not implement TreeNode (wrong type for GetChildren method)
have GetChildren() []*MyStruct
want GetChildren() []TreeNode
这有点令人惊讶,因为接口说GetChildren()
应该返回实现TreeNode
和*MyStruct
确实实现的对象TreeNode
。但很明显,关于编译器如何处理这种情况(递归需要接口类型),我缺少一些东西。
如果我接受编译器的建议(以及这个类似问题的建议)并更改返回MyStruct
的实现,我会在实现中得到一个新的编译器错误说GetChildren
[]TreeNode
GetChildren()
cannot use my_struct.Children (type []*MyStruct) as type []TreeNode in return argument
这也是一个惊喜。另外,共识似乎是我应该struct
从方法中返回 s 而不是返回interface
s。我绝对可以接受[]TreeNode
从那个方法返回,但我一定忽略了一些东西,因为编译器不高兴。
去版本:go version go1.13.8 darwin/amd64
解决方案
“接受接口,返回结构”不是规则,而是一种让生活更轻松的做法。但是,在这种情况下,对于您的接口定义,您没有太多选择。
您可以保留您的设计,但实现 GetChildren:
func (my_struct *MyStruct) GetChildren() []TreeNode {
ret:=make([]TreeNode,0,len(my_struct.Children))
for _,x:=range my_struct.Children {
ret=append(ret,x)
}
return ret
}
这是必需的,因为 Go 的类型系统具有严格的性质。
或者,您可以稍微修改您的设计:
type MyStruct struct {
Name string
Children []TreeNode
}
如果您需要在使用Children
. 但是,它可以让您构建具有不同类型节点的树。
推荐阅读
- javascript - 正则表达式中的条件逻辑
- javascript - Javascript:在浏览器中播放声音:以某种方式坚持用户与此页面交互?
- flutter - 使用 Dart 加密 AES/CBC/PKCS7Padding(在 Kotlin 上工作)
- flutter - 将firestore子集合字段添加到列表中
- reactjs - react-hooks 中的重新渲染问题太多
- java - 使用 jsonpath 过滤嵌套的 json 数据,如示例
- jenkins - 用于多个 repo 的 Jenkins 管道
- handlebars.js - 车把使用数组中的键查找对象?
- batch-file - 使用启动参数启动程序 Sandboxie
- java - Java - 将 MultiSheet Excel 拆分为单独的 Excel,每个 Excel 包含单个工作表