Golang 的匿名接口实现

在 Go 中,是否有一种方法可以匿名地满足接口?看起来没有,但这是我最好的尝试。

(在 游乐场)

package main


import "fmt"


type Thing interface {
Item() float64
SetItem(float64)
}


func newThing() Thing {
item := 0.0
return struct {
Item (func() float64)
SetItem (func(float64))
}{
Item: func() float64 { return item },
SetItem: func(x float64) { item = x },
}
}


func main() {
thing := newThing()
fmt.Println("Hello, playground")
fmt.Println(thing)
}
52631 次浏览

Go 使用 方法集声明哪些方法属于某个类型。使用接收方类型(方法)声明函数只有一种方法:

func (v T) methodName(...) ... { }

由于禁止嵌套函数,因此无法在匿名结构上定义方法集。

第二件不允许这样做的事情是方法是只读的。引入 方法值是为了允许传递方法并在 goroutine 中使用它们,而不是操作方法集。

相反,您可以提供一个 ProtoThing 并引用您的匿名结构(上场了)的底层实现:

type ProtoThing struct {
itemMethod func() float64
setItemMethod func(float64)
}


func (t ProtoThing) Item() float64 { return t.itemMethod() }
func (t ProtoThing) SetItem(x float64) { t.setItemMethod(x) }


// ...


t := struct { ProtoThing }{}


t.itemMethod = func() float64 { return 2.0 }
t.setItemMethod = func(x float64) { item = x }

这是因为通过嵌入 ProtoThing方法集是继承的,因此匿名结构也满足 Thing接口。

这里有一个简单的方法来满足带有匿名函数的接口。

type Thinger interface {
DoThing()
}


type DoThingWith func()


// Satisfy Thinger interface.
// So we can now pass an anonymous function using DoThingWith,
// which implements Thinger.
func (thing DoThingWith) DoThing() {
// delegate to the anonymous function
thing()
}


type App struct {
}


func (a App) DoThing(f Thinger) {
f.DoThing()
}




//...Somewhere else in your code:
app := App{}


// Here we use an anonymous function which satisfies the interface
// The trick here is to convert the anonymous function to the DoThingWith type
// which delegates to the anonymous function


app.DoThing(DoThingWith(func() {
fmt.Println("Hey interface, are you satisfied?")
}))

操场: https://play.golang.org/p/k8_X9g2NYc

Nb,看起来 http 包中的 HandlerFunc 使用了这个模式: https://golang.org/pkg/net/http/#HandlerFunc

编辑: 为了清晰起见,改变类型为 DoThing。更新操场

你不能用方法实例化一个结构,它们需要被声明为函数,但是在 Go 中函数是“一等公民”,所以它们可以是字段值,就像在 JavaScript 中一样(但是类型化)。

您可以创建一个泛型结构,它接受 func 字段来实现接口:

package main


import "fmt"


type Thing interface {
Item() float64
SetItem(float64)
}


// Implements Thing interface
type thingImpl struct {
item    func() float64
setItem func(float64)
}
func (i thingImpl) Item() float64     { return i.item() }
func (i thingImpl) SetItem(v float64) { i.setItem(v) }


func newThing() Thing {
item := 0.0
return thingImpl{
item:    func() float64 { return item },
setItem: func(x float64) { item = x },
}
}


func main() {
thing := newThing()
fmt.Println("Hello, playground")
fmt.Println(thing)
}

当我研究典型的 http 中间件实现如何返回一个 http.Handler接口,而没有为它返回的匿名函数定义一个 ServeHTTP方法时,我来到了这里:

func LoggingMiddleware(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
log.Println(r.Method, r.URL.String())
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}

这里,匿名函数被转换为 HandlerFunc类型以满足接口要求: http.HandlerFunc(fn)

尽管匿名函数并没有通过实现 ServeHTTP方法直接实现 Handler接口本身,但是转换后的 HandlerFunc 类型确实实现了 ServeHTTP方法。

type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}

下面是一个简化的操场例子: https://play.golang.org/p/JX0hrcXyj6Q

package main


import (
"fmt"
)


type Walker interface {
Walk() // interface to be fulfilled by anonymous function
}


type WalkerFunc func()


// Walk method satisfies Walker interface
func (wf WalkerFunc) Walk() {
fmt.Println("start walking")
wf()
fmt.Printf("stop walking\n\n")
}


func main() {
// use type conversion to convert anonymous function to WalkerFunc type, satisfying Walker interface
WalkerFunc(func() { fmt.Println("chew gum") }).Walk()
WalkerFunc(func() { fmt.Println("smell roses") }).Walk()
}


/*
OUTPUT:
start walking
chew gum
stop walking


start walking
smell roses
stop walking
*/