如何在Go语言中给已有的类型添加新方法?

sqxo8psd  于 2023-03-10  发布在  Go
关注(0)|答案(3)|浏览(170)

我想给gorilla/mux Route和Router类型添加一个方便的util方法:

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.Route和mux.Router字段呢?还是别的什么?

uxhixvfz

uxhixvfz1#

正如编译器提到的,你不能扩展另一个包中的现有类型,你可以定义你自己的类型,它由原始类型支持,如下所示:

type MyRouter mux.Router

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

或者通过嵌入原始路由器:

type MyRouter struct {
    *mux.Router
}

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

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

h5qlskok2#

我想详细说明一下@jimt here给出的答案。这个答案是正确的,并且极大地帮助了我解决这个问题。但是,对于我遇到麻烦的两个方法(alias,embed),有一些警告。

  • 注意 *:我使用了parent和child这两个术语,虽然我不确定这对组合是否最好。基本上,parent是您想要在本地修改的类型。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指针取消引用错误。
  • 类型定义和嵌入事例都提供对父级字段的访问。
  • 类型定义不允许访问父级的方法,但嵌入父级可以。

您可以在下面的代码中看到这一点。
working example on the playground

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")
}
dkqlctbz

dkqlctbz3#

扩展其他答案之一,在我的例子中,父对象是一个数组,如果你想添加方法,但同时又想访问父方法,那么你必须在定义类型时换行,在声明变量时换行:

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)
}

相关问题