Golang中继承和方法参数的使用探讨
Golang中继承和方法参数的使用探讨 你好, 我是Go语言的新手,正在尝试将其用于一个关于知识图谱的作业。
我的实现方式如下:
- 我有一个名为Node的类,它包含名称和命名关系。它不应被直接使用。
- 我想要一个Concept类和一个Instance类,它们都“继承”Node。 ……这就是我遇到问题的地方。
因为在Go语言中,Concept不是一个Node,所以我在Node上的AddRelationship方法无法对Concept起作用。
我最初的想法是使用接口,但我有点困惑,而且我尝试的方法似乎不起作用。
那么,以下是我的“类”Node,它是可以工作的:
package main
import "fmt"
// 知识图谱意义上的节点。不应直接使用。
type Node struct {
name string
relationships map[string][]*Node
}
// 节点的构造函数
func NewNode(name string) *Node {
return &Node{
name: name,
// 关系具有名称
relationships: make(map[string][]*Node),
}
}
func (n *Node) GetName() string {
return n.name
}
// 向节点添加关系
func (n *Node) AddRelationship(name string, node *Node) {
n.relationships[name] = append(n.relationships[name], node)
}
// 按名称查找关系
func (n *Node) Find(name string) []*Node {
return n.relationships[name]
}
// 打印节点及其关系
func (n *Node) Print() {
fmt.Println("[", n.name, "]")
for relationName, nodes := range n.relationships {
for _, node := range nodes {
fmt.Println(" └─(", relationName, ")->[", node.GetName(), "]")
}
}
}
简单示例:
package main
func main() {
node := NewNode("Paul")
node.AddRelationship("is-a", NewNode("Humain"))
node.Print()
}
$ go run ./main.go ./node.go
[ Paul ]
└─( is-a )->[ Humain ]
以下是我尝试为Concept类所做的:
定义一个接口
type INode interface {
AddRelationship(name string, node *INode)
Find(name string) []INode
GetName() string
Print()
}
并将之前代码中的每个参数和输出都替换为INode(而不是*Node)。
但即使我实现了所有方法,Concept也没有被识别为INode。
package main
// 概念是知识图谱意义上的节点。
type Concept struct {
Node
}
func NewConcept(name string) *Concept {
return &Concept{
Node: *NewNode(name),
}
}
func (c *Concept) GetName() string {
return c.Node.GetName()
}
// 我读到在参数中,*INode和INode的行为是相同的,因为它是接口?
func (c *Concept) AddRelationship(name string, node INode) {
c.Node.AddRelationship(name, node)
}
func (c *Concept) Find(name string) []INode {
return c.Node.Find(name)
}
func (c *Concept) Print() {
c.Node.Print()
}
那么,在Go的继承系统中,我遗漏了什么呢?
更多关于Golang中继承和方法参数的使用探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
如何指定我的
map[string][]INode变量包含的是指向变量的指针,而不是变量本身?
据我所知,这是无法指定的。
在我的
main.go中(见下文),AddRelationship 的第二个参数是一个指向 Concept 的指针。但我从未指定 AddRelationship 应该接收一个指向值的指针还是一个值本身,对吗?
你没有指定。但由于满足 INode 接口所需的方法是在指针接收器上定义的,这意味着只有指向 Concept 的指针 (*Concept) 才能满足 INode 接口。
如果这有点令人困惑,请看这个例子:Go Playground - The Go Programming Language Given 结构体的方法是在 *Given 上定义的。因此,只有指向 Given 的指针才拥有这些方法。 当我将一个空的 Given 结构体 x 传递给接受 Sample 接口的 checkSample() 函数时,它会失败。将其改为指针 (x := &Given{}) 就能使其正常工作。
首先,Concept 不满足 INode 接口,因为
AddRelationship(name string, node INode)
与
AddRelationship(name string, node *INode)
是不同的。
INode 接口要求 AddRelationship() 的第二个参数是指向满足 INode 接口的类型的指针。而 Concept 的 AddRelationship() 方法的第二个参数要求任何可以满足 INode 接口的东西(可以是值,也可以是指向值的指针)。
关于为什么这两个参数不同的进一步解释:T 和 *T 可以有不同的方法集。
以及为什么指向接口的指针(如 *INode)应该很少使用:何时应该使用指向接口的指针?
其次,看以下代码:
func (n *Node) AddRelationship(name string, node *Node) {
n.relationships[name] = append(n.relationships[name], node)
}
期望的第二个参数是指向特定类型 (*Node) 的指针。
… 然后在:
func (c *Concept) AddRelationship(name string, node INode) {
c.Node.AddRelationship(name, node)
}
第二个参数是任何满足 INode 接口的东西(任何类型或指向任何类型的指针)。然而,它被传递给 c.Node.AddRelationship(name, node),我们知道这需要是具体的 *Node。因此,例如,我有另一个满足 INode 接口的类型 Foo,Concept.AddRelationship() 可以接受它,但 Node.AddRelationship 将无法处理。
你需要使用类型断言来检查接收到的第二个参数是否是 *Node。
非常感谢你的帮助,我现在已经让代码运行起来了!(至少目前是这样)
为什么应该很少使用指向接口的指针(例如 *INode):何时应该使用指向接口的指针?
我认为那是我最大的错误。我现在在所有地方都使用 INode 作为参数(见下面的代码)。但有些地方我不太明白:
- 如何指定我的
map[string][]INode变量包含的是指向变量的指针,而不是变量本身? - 在我的
main.go(见下文)中,AddRelationship 的第二个参数是一个指向 Concept 的指针。但我从未指定 AddRelationship 应该接收一个指向值的指针还是一个值本身,我指定过吗?
node.go
package main
import "fmt"
type INode interface {
AddRelationship(name string, node INode)
Find(name string) []INode
GetName() string
Print()
}
// a node in the sense of a knowledge graph. Shouldn't be used directly.
type Node struct {
name string
relationships map[string][]INode
}
// Constructor of node
func NewNode(name string) *Node {
return &Node{
name: name,
relationships: make(map[string][]INode),
}
}
func (n *Node) GetName() string {
return n.name
}
// Add a relationship to the node
func (n *Node) AddRelationship(name string, node INode) {
n.relationships[name] = append(n.relationships[name], node)
}
// Find relationships by name
func (n *Node) Find(name string) []INode {
return n.relationships[name]
}
// Print the node and its relationships
func (n *Node) Print() {
fmt.Println("[", n.name, "]")
for relationName, nodes := range n.relationships {
for _, node := range nodes {
fmt.Println(" └─(", relationName, ")->[", node.GetName(), "]")
}
}
}
concept.go
package main
// A concept is a node in the sense of a knowledge graph.
type Concept struct {
Node
}
func NewConcept(name string) *Concept {
return &Concept{
Node: *NewNode(name),
}
}
func (c *Concept) GetName() string {
return c.Node.GetName()
}
func (c *Concept) AddRelationship(name string, node INode) {
c.Node.AddRelationship(name, node)
}
func (c *Concept) Find(name string) []INode {
return c.Node.Find(name)
}
func (c *Concept) Print() {
c.Node.Print()
}
main.go
package main
func main() {
human := NewConcept("Human")
woman := NewConcept("Woman")
woman.AddRelationship("is-a", human)
woman.Print()
juliette := NewNode("Juliette")
juliette.AddRelationship("is-a", woman)
juliette.Print()
}
stdout
[ Woman ]
└─( is-a )->[ Human ]
[ Juliette ]
└─( is-a )->[ Woman ]


