嵌套结构体

10

在不使用嵌入式结构的情况下,是否可以继承类型的方法?

第一个代码片段是嵌入Property结构体到Node中的工作代码,并且我能够调用Properties上的node.GetString方法。但我不喜欢的是当我初始化Node时,必须初始化其中的Properties结构体。有没有解决这个问题的方法?

package main

import "fmt"

type Properties map[string]interface{}

func (p Properties) GetString(key string) string {
    return p[key].(string)
}

type Nodes map[string]*Node

type Node struct {
    *Properties
}

func main() {
    allNodes := Nodes{"1": &Node{&Properties{"test": "foo"}}} // :'(
    singleNode := allNodes["1"]
    fmt.Println(singleNode.GetString("test"))
}

最终,我希望能做出以下类似的事情。其中NodeProperties类型,并且初始化不需要初始化Property结构体。下面的代码不能工作,但可以清楚地表达我的目标。

package main

import "fmt"

type Properties map[string]interface{}

func (p Properties) GetString(key string) string {
    return p[key].(string)
}

type Nodes map[string]*Node

type Node Properties

func main() {
    allNodes := Nodes{"1": &Node{"test": "foo"}} // :)
    singleNode := allNodes["1"]
    fmt.Println(singleNode.GetString("test")) // :D
}

我会添加更多的结构,这些结构将使用Properties 的方法,这就是我提出问题的原因。如果我只有 Node,那么我只需为 Node编写方法即可完成。但是因为我将拥有不止一个 Node ,所以我觉得在所有嵌入Properties的结构中添加相同的方法有点冗余。

更确切地说,我想从Node 使用 Properties 的方法,而不必初始化 Properties


1
听起来你可以编写接受 Properties 对象实例并对其进行操作的函数,而不是将它们附加到对象上。这就是 Go 中的嵌入方式...所以我不确定是否有其他方法。组合而非继承。 - Simon Whitehead
嵌入被称为嵌入的原因是它会将所有属性字段嵌入到节点中。 - kostya
2个回答

4
所以,这里你遇到了 Go 的一个怪癖。嵌入是一种方法,其中一个结构体的方法可以“提升”以似乎存在于另一个结构体中。虽然感觉上type Node Properties应该暴露在Node上的Properties方法,但该语法的效果是使Node采用Properties的内存布局,但没有任何方法。

这并没有解释为什么要做出这个设计选择,但Go规范至少是具体而干燥的。如果您按原样阅读它,没有解释,那么它非常准确:

接口类型的方法集是其接口。其他任何类型 T 的方法集由使用 receiver 类型 T 声明的所有方法组成

GetString的接收器类型是Properties而不是Node,严肃地阐释这个规范就像是一位没有想象力的会计师。话虽如此:

包含匿名字段的结构体还适用进一步的规则,如结构体类型的章节所述。

...

结构体x中的匿名字段f(字段或方法)在x.f是合法选择器且表示该字段或方法f时,称为提升的。

提升的字段与结构体的普通字段类似,除了它们不能用作结构体的复合字面量中的字段名称。

给定一个结构体类型S和一个命名类型T,提升的方法包括以下方式被包含在结构体的方法集中:

  • 如果S包含匿名字段T,则S和*S的方法集都包括receiver T的提升方法。*S的方法集也包括receiver *T的提升方法。
  • 如果S包含匿名字段*T,则S和*S的方法集都包括具有接收者T或*T的提升方法。

关于复合字面量的那行指的是这个强制你在创建每个Node时声明Properties的东西。

p.s.嗨 Jeff!


嗨,David!我很想知道为什么做出了那个设计选择。可能在规范中有所提及,只是藏得很深。希望你一切都好。 - Jeff
@Jeff:这里的设计选择是Go语言没有继承,也没有方法重载。嵌入只是一种方便的自动委托方法,具有特定的规则,关于选择器如何提升字段和方法(您也可以直接调用它们),没有任何“继承”。 - JimB

1
你最后一个问题的简短回答是“不”。
中,类型声明和嵌入之间有很大的区别。你可以通过手动将 NodeProperties 进行类型转换来使你的最后一个示例工作。
package main

import "fmt"

type Properties map[string]interface{}

func (p Properties) GetString(key string) string {
    return p[key].(string)
}

type Nodes map[string]*Node

type Node Properties

func main() {
    allNodes := Nodes{"1": &Node{"test": "foo"}} // :)
    singleNode := allNodes["1"]
    fmt.Println(Properties(*singleNode).GetString("test")) // :D
}

但显然这不是你想要的,你想要一个结构体嵌入一个类型别名的语法,这在中是不可能的。我认为你应该坚持你的第一种方法,忽略代码冗余和丑陋的事实。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接