如何在 Go 中向现有类型添加新方法?

我想添加一个方便的 util 方法到 gorilla/mux路由和路由器类型:

package util


import(
"net/http"
"github.com/0xor1/gorillaseed/src/server/lib/mux"
)


func (r *mux.Route) Subroute(tpl string, h http.Handler) *mux.Route{
return r.PathPrefix("/" + tpl).Subrouter().PathPrefix("/").Handler(h)
}


func (r *mux.Router) Subroute(tpl string, h http.Handler) *mux.Route{
return r.PathPrefix("/" + tpl).Subrouter().PathPrefix("/").Handler(h)
}

但是编译器告诉我

无法在非本地类型 mux.Router 上定义新方法

那我该怎么做呢?是否创建一个具有匿名 mux 的新结构类型。路线和音乐。路由器?还是别的什么?

109482 次浏览

正如编译器提到的,您不能扩展另一个包中的现有类型。您可以定义自己的别名或子包如下:

type MyRouter mux.Router


func (m *MyRouter) F() { ... }

或通过嵌入原始路由器:

type MyRouter struct {
*mux.Router
}


func (m *MyRouter) F() { ... }


...
r := &MyRouter{router}
r.F()

我想详细解释一下@jimt 给你给出的答案。这个答案是正确的,并且极大地帮助我理清了这个问题。但是,这两种方法(别名,嵌入)都有一些注意事项,我在使用它们时遇到了麻烦。

注意 : 我使用了“父”和“子”这两个术语,尽管我不确定这对于组合来说是否是最好的。基本上,父类型是您希望在本地修改的类型。Child 是尝试实现该修改的新类型。

方法1-类型定义

type child parent
// or
type MyThing imported.Thing
  • 提供对字段的访问。
  • 不提供对方法的访问。

方法2-嵌入(正式文件)

type child struct {
parent
}
// or with import and pointer
type MyThing struct {
*imported.Thing
}
  • 提供对字段的访问。
  • 提供对方法的访问。
  • 需要考虑初始化。

摘要

  • 使用复合方法时,如果嵌入的父级是指针,则不会进行初始化。父级必须单独初始化。
  • 如果嵌入的父节点是一个指针,并且在初始化子节点时没有初始化,则会发生 nil 指针解引用错误。
  • 类型定义和嵌入用例都提供对父类字段的访问。
  • 类型定义不允许访问父方法,但嵌入父方法可以。

您可以在下面的代码中看到这一点。

操场上的工作示范

package main


import (
"fmt"
)


type parent struct {
attr string
}


type childAlias parent


type childObjParent struct {
parent
}


type childPointerParent struct {
*parent
}


func (p *parent) parentDo(s string) { fmt.Println(s) }
func (c *childAlias) childAliasDo(s string) { fmt.Println(s) }
func (c *childObjParent) childObjParentDo(s string) { fmt.Println(s) }
func (c *childPointerParent) childPointerParentDo(s string) { fmt.Println(s) }


func main() {
p := &parent{"pAttr"}
c1 := &childAlias{"cAliasAttr"}
c2 := &childObjParent{}
// When the parent is a pointer it must be initialized.
// Otherwise, we get a nil pointer error when trying to set the attr.
c3 := &childPointerParent{}
c4 := &childPointerParent{&parent{}}


c2.attr = "cObjParentAttr"
// c3.attr = "cPointerParentAttr" // NOGO nil pointer dereference
c4.attr = "cPointerParentAttr"


// CAN do because we inherit parent's fields
fmt.Println(p.attr)
fmt.Println(c1.attr)
fmt.Println(c2.attr)
fmt.Println(c4.attr)


p.parentDo("called parentDo on parent")
c1.childAliasDo("called childAliasDo on ChildAlias")
c2.childObjParentDo("called childObjParentDo on ChildObjParent")
c3.childPointerParentDo("called childPointerParentDo on ChildPointerParent")
c4.childPointerParentDo("called childPointerParentDo on ChildPointerParent")


// CANNOT do because we don't inherit parent's methods
// c1.parentDo("called parentDo on childAlias") // NOGO c1.parentDo undefined


// CAN do because we inherit the parent's methods
c2.parentDo("called parentDo on childObjParent")
c3.parentDo("called parentDo on childPointerParent")
c4.parentDo("called parentDo on childPointerParent")
}

在另一个答案的基础上展开,在我的例子中,父元素是一个数组 要想添加方法,还要有访问父方法的权限,必须换行 定义类型时,在声明变量时换行:

package main


type parent []int


func (p parent) first() int {
return p[0]
}


type child struct {
parent
}


func (c child) second() int {
return c.parent[1]
}


func main() {
a := child{
parent{1, 2},
}
first := a.first()
second := a.second()
println(first == 1, second == 2)
}